aiobungie

A statically typed, asynchronous API wrapper for building clients for Bungie's API in Python.

Getting Started

This is the basic client you probably want you start with.

import aiobungie

client = aiobungie.Client('YOUR_API_KEY', client_secret='KEY', client_id=0)

async def main() -> None:
    async with client.rest:
        # Search for Destiny 2 memberships.
        users = await client.search_users('Crit')

        # Iterate over the users and take the first 5 results.
        for user in users.take(5):
            # Print the user name and their code.
            print(f'{user.name} {user.code}')

# aiobungie provides an internal function to run async functions.
# It's equivalent to asyncio.run()
client.run(main()) # or asyncio.run(main())
RESTClient

aiobungie provides a second way to use Bungie's API,

a single RESTClient allows you to make requests and return JSON objects immediately.

This bypasses the need to deserialize and create objects. It also exposes all OAuth2 and manifest methods. This can be faster for REST apis.

This is considered the core client since aiobungie.Client is built on top of it. Using the aiobungie.rest property allows direct access to the raw REST client instance.

import aiobungie
import asyncio

client = aiobungie.RESTClient("TOKEN")

async def main() -> None:
    async with client as rest:
        payload = await rest.fetch_membership('Fate怒', 4275)

        for membership in payload:
            print(membership['membershipId'])

asyncio.run(main())
RESTPool

A REST client pool allows you to acquire multiple RESTClient that share the same state.

This is useful when you want to spawn an instance for each client which shared the same state.

import aiobungie
import asyncio

pool = aiobungie.RESTPool("token")

async def set() -> None:
    # Set your ID to access it from other places.
    pool.metadata['my_id'] = 4401
    async with pool.acquire() as instance:
        ...

async def fetch() -> None:
    my_id: int = pool.metadata['my_id']
    async with pool.acquire() as instance: # A different client instance.
        my_user = instance.fetch_bungie_user(my_id)

await asyncio.gather(set(), fetch())

When should you use which client?

  • Use Client when you want to build a Chat Bot, Discord Bot, access data as Python classes.
  • Use RESTClient when you want one TCP session for all clients, access data as JSON payloads.
  • Use RESTPool when you're serving a large amount of connections and want to spawn a session for each, access data as JSON payloads. Note that setting up multiple TCP connections can be expensive.
  1# MIT License
  2#
  3# Copyright (c) 2020 - Present nxtlo
  4#
  5# Permission is hereby granted, free of charge, to any person obtaining a copy
  6# of this software and associated documentation files (the "Software"), to deal
  7# in the Software without restriction, including without limitation the rights
  8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9# copies of the Software, and to permit persons to whom the Software is
 10# furnished to do so, subject to the following conditions:
 11#
 12# The above copyright notice and this permission notice shall be included in all
 13# copies or substantial portions of the Software.
 14#
 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 21# SOFTWARE.
 22
 23"""A statically typed, asynchronous API wrapper for building clients for Bungie's API in Python.
 24
 25Getting Started
 26---------------
 27
 28This is the basic client you probably want you start with.
 29
 30```py
 31import aiobungie
 32
 33client = aiobungie.Client('YOUR_API_KEY', client_secret='KEY', client_id=0)
 34
 35async def main() -> None:
 36    async with client.rest:
 37        # Search for Destiny 2 memberships.
 38        users = await client.search_users('Crit')
 39
 40        # Iterate over the users and take the first 5 results.
 41        for user in users.take(5):
 42            # Print the user name and their code.
 43            print(f'{user.name} {user.code}')
 44
 45# aiobungie provides an internal function to run async functions.
 46# It's equivalent to asyncio.run()
 47client.run(main()) # or asyncio.run(main())
 48```
 49
 50RESTClient
 51----------
 52
 53aiobungie provides a second way to use Bungie's API,
 54
 55a single `RESTClient` allows you to make requests and return JSON objects immediately.
 56
 57This bypasses the need to deserialize and create objects. It also exposes all `OAuth2` and `manifest` methods.
 58This can be faster for `REST` apis.
 59
 60This is considered the core client since `aiobungie.Client` is built on top of it.
 61Using the `.rest` property allows direct access to the raw REST client instance.
 62
 63
 64```py
 65import aiobungie
 66import asyncio
 67
 68client = aiobungie.RESTClient("TOKEN")
 69
 70async def main() -> None:
 71    async with client as rest:
 72        payload = await rest.fetch_membership('Fate怒', 4275)
 73
 74        for membership in payload:
 75            print(membership['membershipId'])
 76
 77asyncio.run(main())
 78```
 79
 80RESTPool
 81--------
 82
 83A REST client pool allows you to acquire multiple `RESTClient` that share the same state.
 84
 85This is useful when you want to spawn an instance for each client which shared the same state.
 86
 87```py
 88import aiobungie
 89import asyncio
 90
 91pool = aiobungie.RESTPool("token")
 92
 93async def set() -> None:
 94    # Set your ID to access it from other places.
 95    pool.metadata['my_id'] = 4401
 96    async with pool.acquire() as instance:
 97        ...
 98
 99async def fetch() -> None:
100    my_id: int = pool.metadata['my_id']
101    async with pool.acquire() as instance: # A different client instance.
102        my_user = instance.fetch_bungie_user(my_id)
103
104await asyncio.gather(set(), fetch())
105```
106
107When should you use which client?
108---------------------------------
109* Use `Client` when you want to build a Chat Bot, Discord Bot, access data as Python classes.
110* Use `RESTClient` when you want one TCP session for all clients, access data as JSON payloads.
111* Use `RESTPool` when you're serving a large amount of connections and want to spawn a session for each,
112access data as JSON payloads.
113Note that setting up multiple TCP connections can be expensive.
114"""
115
116from __future__ import annotations
117
118from aiobungie import builders
119from aiobungie import crates
120from aiobungie import interfaces
121from aiobungie import traits
122from aiobungie import typedefs
123from aiobungie import url
124from aiobungie.client import Client
125from aiobungie.error import *
126from aiobungie.internal import iterators
127from aiobungie.internal.assets import Image
128from aiobungie.internal.enums import *
129from aiobungie.internal.factory import EmptyFactory
130from aiobungie.internal.factory import Factory
131from aiobungie.internal.iterators import *
132from aiobungie.rest import *
133
134from .metadata import __about__
135from .metadata import __author__
136from .metadata import __docs__
137from .metadata import __email__
138from .metadata import __license__
139from .metadata import __url__
140from .metadata import __version__
141
142# Alias for crate for backwards compatibility.
143crate = crates
144
145# Activity enums
146from .crates.activity import Difficulty
147
148# Components enums
149from .crates.components import ComponentFields
150from .crates.components import ComponentPrivacy
151
152# Entity enums
153from .crates.entity import GatingScope
154from .crates.entity import ObjectiveUIStyle
155from .crates.entity import ValueUIStyle
156
157# Fireteam enums.
158from .crates.fireteams import FireteamActivity
159from .crates.fireteams import FireteamDate
160from .crates.fireteams import FireteamLanguage
161from .crates.fireteams import FireteamPlatform
162
163# Records enums
164from .crates.records import RecordState
165
166__all__ = [mod for mod in dir() if not mod.startswith("_")]  # type: ignore
@attrs.define(auto_exc=True)
class AiobungieError(builtins.RuntimeError):
74@attrs.define(auto_exc=True)
75class AiobungieError(RuntimeError):
76    """Base class that all other exceptions inherit from."""

Base class that all other exceptions inherit from.

AiobungieError()
2def __init__(self, ):
3    BaseException.__init__(self, )

Method generated by attrs for class AiobungieError.

Inherited Members
builtins.BaseException
with_traceback
args
@typing.final
class AmmoType(builtins.int, aiobungie.Enum):
634@typing.final
635class AmmoType(int, Enum):
636    """AN enum for Detyiny 2 ammo types."""
637
638    NONE = 0
639    PRIMARY = 1
640    SPECIAL = 2
641    HEAVY = 3

AN enum for Detyiny 2 ammo types.

NONE = <AmmoType.NONE: 0>
PRIMARY = <AmmoType.PRIMARY: 1>
SPECIAL = <AmmoType.SPECIAL: 2>
HEAVY = <AmmoType.HEAVY: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class BadRequest(aiobungie.HTTPError):
164@attrs.define(auto_exc=True)
165class BadRequest(HTTPError):
166    """An exception raised when requesting a resource with the provided data is wrong."""
167
168    url: typedefs.StrOrURL | None
169    """The URL/endpoint caused this error."""
170
171    body: typing.Any
172    """The response body."""
173
174    headers: multidict.CIMultiDictProxy[str]
175    """The response headers."""
176
177    http_status: http.HTTPStatus = attrs.field(
178        default=http.HTTPStatus.BAD_REQUEST, init=False
179    )

An exception raised when requesting a resource with the provided data is wrong.

BadRequest( message: str, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str])
2def __init__(self, message, url, body, headers):
3    self.message = message
4    self.url = url
5    self.body = body
6    self.headers = headers
7    self.http_status = attr_dict['http_status'].default
8    BaseException.__init__(self, self.message,self.url,self.body,self.headers)

Method generated by attrs for class BadRequest.

url: Union[str, yarl.URL, NoneType]

The URL/endpoint caused this error.

body: Any

The response body.

headers: multidict._multidict.CIMultiDictProxy[str]

The response headers.

http_status: http.HTTPStatus

The response status.

Inherited Members
HTTPError
message
builtins.BaseException
with_traceback
args
@typing.final
class ClanMemberType(builtins.int, aiobungie.Enum):
689@typing.final
690class ClanMemberType(int, Enum):
691    """An enum for bungie clan member types."""
692
693    NONE = 0
694    BEGINNER = 1
695    MEMBER = 2
696    ADMIN = 3
697    ACTING_FOUNDER = 4
698    FOUNDER = 5

An enum for bungie clan member types.

NONE = <ClanMemberType.NONE: 0>
BEGINNER = <ClanMemberType.BEGINNER: 1>
MEMBER = <ClanMemberType.MEMBER: 2>
ADMIN = <ClanMemberType.ADMIN: 3>
ACTING_FOUNDER = <ClanMemberType.ACTING_FOUNDER: 4>
FOUNDER = <ClanMemberType.FOUNDER: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Class(builtins.int, aiobungie.Enum):
465@typing.final
466class Class(int, Enum):
467    """An Enum for Destiny character classes."""
468
469    TITAN = 0
470    HUNTER = 1
471    WARLOCK = 2
472    UNKNOWN = 3

An Enum for Destiny character classes.

TITAN = <Class.TITAN: 0>
HUNTER = <Class.HUNTER: 1>
WARLOCK = <Class.WARLOCK: 2>
UNKNOWN = <Class.UNKNOWN: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Client(aiobungie.traits.ClientApp):
  58class Client(traits.ClientApp):
  59    """Standard Bungie API client application.
  60
  61    This client deserialize the REST JSON responses using `aiobungie.internal.factory.Factory`
  62    and returns `aiobungie.crates` Python object implementations of the responses.
  63
  64    A `aiobungie.RESTClient` REST client can also be used alone for low-level concepts.
  65
  66    Example
  67    -------
  68    ```py
  69    import aiobungie
  70
  71    client = aiobungie.Client('...')
  72
  73    async def main():
  74        async with client.rest:
  75            user = await client.fetch_current_user_memberships('...')
  76            print(user)
  77    ```
  78
  79    Parameters
  80    -----------
  81    token: `str`
  82        Your Bungie's API key or Token from the developer's portal.
  83
  84    Other Parameters
  85    ----------------
  86    max_retries : `int`
  87        The max retries number to retry if the request hit a `5xx` status code.
  88    client_secret : `str | None`
  89        An optional application client secret,
  90        This is only needed if you're fetching OAuth2 tokens with this client.
  91    client_id : `int | None`
  92        An optional application client id,
  93        This is only needed if you're fetching OAuth2 tokens with this client.
  94    """
  95
  96    __slots__ = ("_rest", "_factory")
  97
  98    def __init__(
  99        self,
 100        token: str,
 101        /,
 102        *,
 103        client_secret: str | None = None,
 104        client_id: int | None = None,
 105        max_retries: int = 4,
 106    ) -> None:
 107        self._rest = rest_.RESTClient(
 108            token,
 109            client_secret=client_secret,
 110            client_id=client_id,
 111            max_retries=max_retries,
 112        )
 113
 114        self._factory = factory_.Factory(self)
 115
 116    @property
 117    def factory(self) -> factory_.Factory:
 118        return self._factory
 119
 120    @property
 121    def rest(self) -> interfaces.RESTInterface:
 122        return self._rest
 123
 124    @property
 125    def request(self) -> Client:
 126        return self
 127
 128    @property
 129    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
 130        return self._rest.metadata
 131
 132    def run(self, fn: collections.Awaitable[typing.Any], debug: bool = False) -> None:
 133        loop = helpers.get_or_make_loop()
 134
 135        try:
 136            if not loop.is_running():
 137                loop.set_debug(debug)
 138                loop.run_until_complete(fn)
 139
 140        except Exception as exc:
 141            raise RuntimeError(f"Failed to run {fn!s}") from exc
 142
 143        except KeyboardInterrupt:
 144            _LOG.warn("Unexpected Keyboard interrupt. Exiting.")
 145
 146    # * User methods.
 147
 148    async def fetch_current_user_memberships(self, access_token: str, /) -> user.User:
 149        """Fetch and return a user object of the bungie net user associated with account.
 150
 151        .. warning::
 152            This method requires OAuth2 scope and a Bearer access token.
 153
 154        Parameters
 155        ----------
 156        access_token : `str`
 157            A valid Bearer access token for the authorization.
 158
 159        Returns
 160        -------
 161        `aiobungie.crates.user.User`
 162            A user object includes the Destiny memberships and Bungie.net user.
 163        """
 164        resp = await self.rest.fetch_current_user_memberships(access_token)
 165
 166        return self.factory.deserialize_user(resp)
 167
 168    async def fetch_bungie_user(self, id: int, /) -> user.BungieUser:
 169        """Fetch a Bungie user by their BungieNet id.
 170
 171        .. note::
 172            This returns a Bungie user membership only. Take a look at `Client.fetch_membership_from_id`
 173            for other memberships.
 174
 175        Parameters
 176        ----------
 177        id: `int`
 178            The user id.
 179
 180        Returns
 181        -------
 182        `aiobungie.crates.user.BungieUser`
 183            A Bungie user.
 184
 185        Raises
 186        ------
 187        `aiobungie.error.NotFound`
 188            The user was not found.
 189        """
 190        payload = await self.rest.fetch_bungie_user(id)
 191
 192        return self.factory.deserialize_bungie_user(payload)
 193
 194    async def search_users(
 195        self, name: str, /
 196    ) -> iterators.Iterator[user.SearchableDestinyUser]:
 197        """Search for players and return all players that matches the same name.
 198
 199        Parameters
 200        ----------
 201        name : `buildins.str`
 202            The user name.
 203
 204        Returns
 205        -------
 206        `aiobungie.Iterator[aiobungie.crates.DestinyMembership]`
 207            A sequence of destiny memberships.
 208        """
 209        payload = await self.rest.search_users(name)
 210
 211        return iterators.Iterator(
 212            [
 213                self.factory.deserialize_searched_user(user)
 214                for user in payload["searchResults"]
 215            ]
 216        )
 217
 218    async def fetch_user_themes(self) -> collections.Sequence[user.UserThemes]:
 219        """Fetch all available user themes.
 220
 221        Returns
 222        -------
 223        `collections.Sequence[aiobungie.crates.user.UserThemes]`
 224            A sequence of user themes.
 225        """
 226        data = await self.rest.fetch_user_themes()
 227
 228        return self.factory.deserialize_user_themes(data)
 229
 230    async def fetch_hard_types(
 231        self,
 232        credential: int,
 233        type: enums.CredentialType | int = enums.CredentialType.STEAMID,
 234        /,
 235    ) -> user.HardLinkedMembership:
 236        """Gets any hard linked membership given a credential.
 237        Only works for credentials that are public just `aiobungie.CredentialType.STEAMID` right now.
 238        Cross Save aware.
 239
 240        Parameters
 241        ----------
 242        credential: `int`
 243            A valid SteamID64
 244        type: `aiobungie.CredentialType`
 245            The credential type. This must not be changed
 246            Since its only credential that works "currently"
 247
 248        Returns
 249        -------
 250        `aiobungie.crates.user.HardLinkedMembership`
 251            Information about the hard linked data.
 252        """
 253
 254        payload = await self.rest.fetch_hardlinked_credentials(credential, type)
 255
 256        return user.HardLinkedMembership(
 257            id=int(payload["membershipId"]),
 258            type=enums.MembershipType(payload["membershipType"]),
 259            cross_save_type=enums.MembershipType(payload["CrossSaveOverriddenType"]),
 260        )
 261
 262    async def fetch_membership_from_id(
 263        self,
 264        id: int,
 265        /,
 266        type: enums.MembershipType | int = enums.MembershipType.NONE,
 267    ) -> user.User:
 268        """Fetch Bungie user's memberships from their id.
 269
 270        Notes
 271        -----
 272        * This returns both BungieNet membership and a sequence of the player's DestinyMemberships
 273        Which includes Stadia, Xbox, Steam and PSN memberships if the player has them,
 274        see `aiobungie.crates.user.DestinyMembership` for more details.
 275        * If you only want the bungie user. Consider using `Client.fetch_user` method.
 276
 277        Parameters
 278        ----------
 279        id : `int`
 280            The user's id.
 281        type : `aiobungie.MembershipType`
 282            The user's membership type.
 283
 284        Returns
 285        -------
 286        `aiobungie.crates.User`
 287            A Bungie user with their membership types.
 288
 289        Raises
 290        ------
 291        aiobungie.NotFound
 292            The requested user was not found.
 293        """
 294        payload = await self.rest.fetch_membership_from_id(id, type)
 295
 296        return self.factory.deserialize_user(payload)
 297
 298    async def fetch_user_credentials(
 299        self, access_token: str, membership_id: int, /
 300    ) -> collections.Sequence[user.UserCredentials]:
 301        """Fetch an array of credential types attached to the requested account.
 302
 303        .. note::
 304            This method require OAuth2 Bearer access token.
 305
 306        Parameters
 307        ----------
 308        access_token : `str`
 309            The bearer access token associated with the bungie account.
 310        membership_id : `int`
 311            The id of the membership to return.
 312
 313        Returns
 314        -------
 315        `collections.Sequence[aiobungie.crates.UserCredentials]`
 316            A sequence of the attached user credentials.
 317
 318        Raises
 319        ------
 320        `aiobungie.Unauthorized`
 321            The access token was wrong or no access token passed.
 322        """
 323        resp = await self.rest.fetch_user_credentials(access_token, membership_id)
 324
 325        return self.factory.deserialize_user_credentials(resp)
 326
 327    # * Destiny 2.
 328
 329    async def fetch_profile(
 330        self,
 331        member_id: int,
 332        type: enums.MembershipType | int,
 333        components: list[enums.ComponentType],
 334        auth: str | None = None,
 335    ) -> components.Component:
 336        """
 337        Fetch a bungie profile passing components to the request.
 338
 339        Parameters
 340        ----------
 341        member_id: `int`
 342            The member's id.
 343        type: `aiobungie.MembershipType`
 344            A valid membership type.
 345        components : `list[aiobungie.ComponentType]`
 346            List of profile components to collect and return.
 347
 348        Other Parameters
 349        ----------------
 350        auth : `str | None`
 351            A Bearer access_token to make the request with.
 352            This is optional and limited to components that only requires an Authorization token.
 353
 354        Returns
 355        --------
 356        `aiobungie.crates.Component`
 357            A Destiny 2 player profile with its components.
 358            Only passed components will be available if they exists. Otherwise they will be `None`
 359
 360        Raises
 361        ------
 362        `aiobungie.MembershipTypeError`
 363            The provided membership type was invalid.
 364        """
 365        data = await self.rest.fetch_profile(member_id, type, components, auth)
 366        return self.factory.deserialize_components(data)
 367
 368    async def fetch_linked_profiles(
 369        self,
 370        member_id: int,
 371        member_type: enums.MembershipType | int,
 372        /,
 373        *,
 374        all: bool = False,
 375    ) -> profile.LinkedProfile:
 376        """Returns a summary information about all profiles linked to the requested member.
 377
 378        The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.
 379
 380        .. note::
 381            It will only return linked accounts whose linkages you are allowed to view.
 382
 383        Parameters
 384        ----------
 385        member_id : `int`
 386            The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
 387        member_type : `aiobungie.MembershipType`
 388            The type for the membership whose linked Destiny account you want to return.
 389
 390        Other Parameters
 391        ----------------
 392        all : `bool`
 393            If provided and set to `True`, All memberships regardless
 394            of whether they're obscured by overrides will be returned,
 395
 396            If provided and set to `False`, Only available memberships will be returned.
 397            The default for this is `False`.
 398
 399        Returns
 400        -------
 401        `aiobungie.crates.profile.LinkedProfile`
 402            A linked profile object.
 403        """
 404        resp = await self.rest.fetch_linked_profiles(member_id, member_type, all=all)
 405
 406        return self.factory.deserialize_linked_profiles(resp)
 407
 408    async def fetch_membership(
 409        self,
 410        name: str,
 411        code: int,
 412        /,
 413        type: enums.MembershipType | int = enums.MembershipType.ALL,
 414    ) -> collections.Sequence[user.DestinyMembership]:
 415        """Fetch a Destiny 2 player's memberships.
 416
 417        Parameters
 418        -----------
 419        name: `str`
 420            The unique Bungie player name.
 421        code : `int`
 422            The unique Bungie display name code.
 423        type: `aiobungie.internal.enums.MembershipType`
 424            The player's membership type, e,g. XBOX, STEAM, PSN
 425
 426        Returns
 427        --------
 428        `collections.Sequence[aiobungie.crates.DestinyMembership]`
 429            A sequence of the found Destiny 2 player memberships.
 430            An empty sequence will be returned if no one found.
 431
 432        Raises
 433        ------
 434        `aiobungie.MembershipTypeError`
 435            The provided membership type was invalid.
 436        """
 437        resp = await self.rest.fetch_membership(name, code, type)
 438
 439        return self.factory.deserialize_destiny_memberships(resp)
 440
 441    async def fetch_character(
 442        self,
 443        member_id: int,
 444        membership_type: enums.MembershipType | int,
 445        character_id: int,
 446        components: list[enums.ComponentType],
 447        auth: str | None = None,
 448    ) -> components.CharacterComponent:
 449        """Fetch a Destiny 2 character.
 450
 451        Parameters
 452        ----------
 453        member_id: `int`
 454            A valid bungie member id.
 455        character_id: `int`
 456            The Destiny character id to retrieve.
 457        membership_type: `aiobungie.internal.enums.MembershipType`
 458            The member's membership type.
 459        components: `list[aiobungie.ComponentType]`
 460            Multiple arguments of character components to collect and return.
 461
 462        Other Parameters
 463        ----------------
 464        auth : `str | None`
 465            A Bearer access_token to make the request with.
 466            This is optional and limited to components that only requires an Authorization token.
 467
 468        Returns
 469        -------
 470        `aiobungie.crates.CharacterComponent`
 471            A Bungie character component.
 472
 473        `aiobungie.MembershipTypeError`
 474            The provided membership type was invalid.
 475        """
 476        resp = await self.rest.fetch_character(
 477            member_id, membership_type, character_id, components, auth
 478        )
 479
 480        return self.factory.deserialize_character_component(resp)
 481
 482    async def fetch_unique_weapon_history(
 483        self,
 484        membership_id: int,
 485        character_id: int,
 486        membership_type: enums.MembershipType | int,
 487    ) -> collections.Sequence[activity.ExtendedWeaponValues]:
 488        """Fetch details about unique weapon usage for a character. Includes all exotics.
 489
 490        Parameters
 491        ----------
 492        membership_id : `int`
 493            The Destiny user membership id.
 494        character_id : `int`
 495            The character id to retrieve.
 496        membership_type : `aiobungie.aiobungie.MembershipType | int`
 497            The Destiny user's membership type.
 498
 499        Returns
 500        -------
 501        `collections.Sequence[aiobungie.crates.ExtendedWeaponValues]`
 502            A sequence of the weapon's extended values.
 503        """
 504        resp = await self._rest.fetch_unique_weapon_history(
 505            membership_id, character_id, membership_type
 506        )
 507
 508        return [
 509            self._factory.deserialize_extended_weapon_values(weapon)
 510            for weapon in resp["weapons"]
 511        ]
 512
 513    # * Destiny 2 Activities.
 514
 515    async def fetch_activities(
 516        self,
 517        member_id: int,
 518        character_id: int,
 519        mode: enums.GameMode | int,
 520        *,
 521        membership_type: enums.MembershipType | int = enums.MembershipType.ALL,
 522        page: int = 0,
 523        limit: int = 250,
 524    ) -> iterators.Iterator[activity.Activity]:
 525        """Fetch a Destiny 2 activity for the specified character id.
 526
 527        Parameters
 528        ----------
 529        member_id: `int`
 530            The user id that starts with `4611`.
 531        character_id: `int`
 532            The id of the character to retrieve the activities for.
 533        mode: `aiobungie.aiobungie.internal.enums.GameMode | int`
 534            This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
 535
 536        Other Parameters
 537        ----------------
 538        membership_type: `aiobungie.internal.enums.MembershipType`
 539            The Member ship type, if nothing was passed than it will return all.
 540        page: int
 541            The page number. Default is `0`
 542        limit: int
 543            Limit the returned result. Default is `250`.
 544
 545        Returns
 546        -------
 547        `aiobungie.Iterator[aiobungie.crates.Activity]`
 548            An iterator of the player's activities.
 549
 550        Raises
 551        ------
 552        `aiobungie.MembershipTypeError`
 553            The provided membership type was invalid.
 554        """
 555        resp = await self.rest.fetch_activities(
 556            member_id,
 557            character_id,
 558            mode,
 559            membership_type=membership_type,
 560            page=page,
 561            limit=limit,
 562        )
 563
 564        return self.factory.deserialize_activities(resp)
 565
 566    async def fetch_post_activity(self, instance_id: int, /) -> activity.PostActivity:
 567        """Fetch a post activity details.
 568
 569        Parameters
 570        ----------
 571        instance_id: `int`
 572            The activity instance id.
 573
 574        Returns
 575        -------
 576        `aiobungie.crates.PostActivity`
 577           A post activity object.
 578        """
 579        resp = await self.rest.fetch_post_activity(instance_id)
 580
 581        return self.factory.deserialize_post_activity(resp)
 582
 583    async def fetch_aggregated_activity_stats(
 584        self,
 585        character_id: int,
 586        membership_id: int,
 587        membership_type: enums.MembershipType | int,
 588    ) -> iterators.Iterator[activity.AggregatedActivity]:
 589        """Fetch aggregated activity stats for a character.
 590
 591        Parameters
 592        ----------
 593        character_id: `int`
 594            The id of the character to retrieve the activities for.
 595        membership_id: `int`
 596            The id of the user that started with `4611`.
 597        membership_type: `aiobungie.internal.enums.MembershipType`
 598            The Member ship type.
 599
 600        Returns
 601        -------
 602        `aiobungie.Iterator[aiobungie.crates.AggregatedActivity]`
 603            An iterator of the player's activities.
 604
 605        Raises
 606        ------
 607        `aiobungie.MembershipTypeError`
 608            The provided membership type was invalid.
 609        """
 610        resp = await self.rest.fetch_aggregated_activity_stats(
 611            character_id, membership_id, membership_type
 612        )
 613
 614        return self.factory.deserialize_aggregated_activities(resp)
 615
 616    # * Destiny 2 Clans or GroupsV2.
 617
 618    async def fetch_clan_from_id(
 619        self,
 620        id: int,
 621        /,
 622        access_token: str | None = None,
 623    ) -> clans.Clan:
 624        """Fetch a Bungie Clan by its id.
 625
 626        Parameters
 627        -----------
 628        id: `int`
 629            The clan id.
 630
 631        Returns
 632        --------
 633        `aiobungie.crates.Clan`
 634            An Bungie clan.
 635
 636        Raises
 637        ------
 638        `aiobungie.NotFound`
 639            The clan was not found.
 640        """
 641        resp = await self.rest.fetch_clan_from_id(id, access_token)
 642
 643        return self.factory.deserialize_clan(resp)
 644
 645    async def fetch_clan(
 646        self,
 647        name: str,
 648        /,
 649        access_token: str | None = None,
 650        *,
 651        type: enums.GroupType | int = enums.GroupType.CLAN,
 652    ) -> clans.Clan:
 653        """Fetch a Clan by its name.
 654        This method will return the first clan found with given name.
 655
 656        Parameters
 657        ----------
 658        name: `str`
 659            The clan name
 660
 661        Other Parameters
 662        ----------------
 663        access_token : `str | None`
 664            An optional access token to make the request with.
 665
 666            If the token was bound to a member of the clan,
 667            This field `aiobungie.crates.Clan.current_user_membership` will be available
 668            and will return the membership of the user who made this request.
 669        type : `aiobungie.GroupType`
 670            The group type, Default is aiobungie.GroupType.CLAN.
 671
 672        Returns
 673        -------
 674        `aiobungie.crates.Clan`
 675            A Bungie clan.
 676
 677        Raises
 678        ------
 679        `aiobungie.NotFound`
 680            The clan was not found.
 681        """
 682        resp = await self.rest.fetch_clan(name, access_token, type=type)
 683
 684        return self.factory.deserialize_clan(resp)
 685
 686    async def fetch_clan_conversations(
 687        self, clan_id: int, /
 688    ) -> collections.Sequence[clans.ClanConversation]:
 689        """Fetch the conversations/chat channels of the given clan id.
 690
 691        Parameters
 692        ----------
 693        clan_id : `int`
 694            The clan id.
 695
 696        Returns
 697        `collections.Sequence[aiobungie.crates.ClanConversation]`
 698            A sequence of the clan chat channels.
 699        """
 700        resp = await self.rest.fetch_clan_conversations(clan_id)
 701
 702        return self.factory.deserialize_clan_conversations(resp)
 703
 704    async def fetch_clan_admins(
 705        self, clan_id: int, /
 706    ) -> iterators.Iterator[clans.ClanMember]:
 707        """Fetch the clan founder and admins.
 708
 709        Parameters
 710        ----------
 711        clan_id : `int`
 712            The clan id.
 713
 714        Returns
 715        -------
 716        `aiobungie.Iterator[aiobungie.crates.ClanMember]`
 717            An iterator over the found clan admins and founder.
 718
 719        Raises
 720        ------
 721        `aiobungie.NotFound`
 722            The requested clan was not found.
 723        """
 724        resp = await self.rest.fetch_clan_admins(clan_id)
 725
 726        return self.factory.deserialize_clan_members(resp)
 727
 728    async def fetch_groups_for_member(
 729        self,
 730        member_id: int,
 731        member_type: enums.MembershipType | int,
 732        /,
 733        *,
 734        filter: int = 0,
 735        group_type: enums.GroupType = enums.GroupType.CLAN,
 736    ) -> collections.Sequence[clans.GroupMember]:
 737        """Fetch information about the groups that a given member has joined.
 738
 739        Parameters
 740        ----------
 741        member_id : `int`
 742            The member's id
 743        member_type : `aiobungie.MembershipType`
 744            The member's membership type.
 745
 746        Other Parameters
 747        ----------------
 748        filter : `int`
 749            Filter apply to list of joined groups. This Default to `0`
 750        group_type : `aiobungie.GroupType`
 751            The group's type.
 752            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
 753
 754        Returns
 755        -------
 756        `collections.Sequence[aiobungie.crates.GroupMember]`
 757            A sequence of joined groups for the fetched member.
 758        """
 759        resp = await self.rest.fetch_groups_for_member(
 760            member_id, member_type, filter=filter, group_type=group_type
 761        )
 762
 763        return [
 764            self.factory.deserialize_group_member(group) for group in resp["results"]
 765        ]
 766
 767    async def fetch_potential_groups_for_member(
 768        self,
 769        member_id: int,
 770        member_type: enums.MembershipType | int,
 771        /,
 772        *,
 773        filter: int = 0,
 774        group_type: enums.GroupType | int = enums.GroupType.CLAN,
 775    ) -> collections.Sequence[clans.GroupMember]:
 776        """Fetch the potential groups for a clan member.
 777
 778        Parameters
 779        ----------
 780        member_id : `int`
 781            The member's id
 782        member_type : `aiobungie.aiobungie.MembershipType | int`
 783            The member's membership type.
 784
 785        Other Parameters
 786        ----------------
 787        filter : `int`
 788            Filter apply to list of joined groups. This Default to `0`
 789        group_type : `aiobungie.aiobungie.GroupType | int`
 790            The group's type.
 791            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
 792
 793        Returns
 794        -------
 795        `collections.Sequence[aiobungie.crates.GroupMember]`
 796            A sequence of joined potential groups for the fetched member.
 797        """
 798        resp = await self.rest.fetch_potential_groups_for_member(
 799            member_id, member_type, filter=filter, group_type=group_type
 800        )
 801
 802        return [
 803            self.factory.deserialize_group_member(group) for group in resp["results"]
 804        ]
 805
 806    async def fetch_clan_members(
 807        self,
 808        clan_id: int,
 809        /,
 810        *,
 811        name: str | None = None,
 812        type: enums.MembershipType | int = enums.MembershipType.NONE,
 813    ) -> iterators.Iterator[clans.ClanMember]:
 814        """Fetch Bungie clan members.
 815
 816        Parameters
 817        ----------
 818        clan_id : `int`
 819            The clans id
 820
 821        Other Parameters
 822        ----------------
 823        name : `str | None`
 824            If provided, Only players matching this name will be returned.
 825        type : `aiobungie.MembershipType`
 826            An optional clan member's membership type.
 827            This parameter is used to filter the returned results
 828            by the provided membership, For an example XBox memberships only,
 829            Otherwise will return all memberships.
 830
 831        Returns
 832        -------
 833        `aiobungie.Iterator[aiobungie.crates.ClanMember]`
 834            An iterator over the bungie clan members.
 835
 836        Raises
 837        ------
 838        `aiobungie.NotFound`
 839            The clan was not found.
 840        """
 841        resp = await self.rest.fetch_clan_members(clan_id, type=type, name=name)
 842
 843        return self.factory.deserialize_clan_members(resp)
 844
 845    async def fetch_clan_banners(self) -> collections.Sequence[clans.ClanBanner]:
 846        """Fetch the clan banners.
 847
 848        Returns
 849        -------
 850        `collections.Sequence[aiobungie.crates.ClanBanner]`
 851            A sequence of the clan banners.
 852        """
 853        resp = await self.rest.fetch_clan_banners()
 854
 855        return self.factory.deserialize_clan_banners(resp)
 856
 857    # This method is required to be here since it deserialize the clan.
 858    async def kick_clan_member(
 859        self,
 860        access_token: str,
 861        /,
 862        group_id: int,
 863        membership_id: int,
 864        membership_type: enums.MembershipType | int,
 865    ) -> clans.Clan:
 866        """Kick a member from the clan.
 867
 868        .. note::
 869            This request requires OAuth2: oauth2: `AdminGroups` scope.
 870
 871        Parameters
 872        ----------
 873        access_token : `str`
 874            The bearer access token associated with the bungie account.
 875        group_id: `int`
 876            The group id.
 877        membership_id : `int`
 878            The member id to kick.
 879        membership_type : `aiobungie.aiobungie.MembershipType | int`
 880            The member's membership type.
 881
 882        Returns
 883        -------
 884        `aiobungie.crates.clan.Clan`
 885            The clan that the member was kicked from.
 886        """
 887        resp = await self.rest.kick_clan_member(
 888            access_token,
 889            group_id=group_id,
 890            membership_id=membership_id,
 891            membership_type=membership_type,
 892        )
 893
 894        return self.factory.deserialize_clan(resp)
 895
 896    async def fetch_clan_weekly_rewards(self, clan_id: int) -> milestones.Milestone:
 897        """Fetch a Bungie clan's weekly reward state.
 898
 899        Parameters
 900        ----------
 901        clan_id : `int`
 902            The clan's id.
 903
 904        Returns
 905        -------
 906        `aiobungie.crates.Milestone`
 907            A runtime status of the clan's milestone data.
 908        """
 909
 910        resp = await self.rest.fetch_clan_weekly_rewards(clan_id)
 911
 912        return self.factory.deserialize_milestone(resp)
 913
 914    # * Destiny 2 Entities aka Definitions.
 915
 916    async def fetch_inventory_item(self, hash: int, /) -> entity.InventoryEntity:
 917        """Fetch a static inventory item entity given a its hash.
 918
 919        Parameters
 920        ----------
 921        hash: `int`
 922            Inventory item's hash.
 923
 924        Returns
 925        -------
 926        `aiobungie.crates.InventoryEntity`
 927            A bungie inventory item.
 928        """
 929        resp = await self.rest.fetch_inventory_item(hash)
 930
 931        return self.factory.deserialize_inventory_entity(resp)
 932
 933    async def fetch_objective_entity(self, hash: int, /) -> entity.ObjectiveEntity:
 934        """Fetch a Destiny objective entity given a its hash.
 935
 936        Parameters
 937        ----------
 938        hash: `int`
 939            objective's hash.
 940
 941        Returns
 942        -------
 943        `aiobungie.crates.ObjectiveEntity`
 944            An objective entity item.
 945        """
 946        resp = await self.rest.fetch_objective_entity(hash)
 947
 948        return self.factory.deserialize_objective_entity(resp)
 949
 950    async def search_entities(
 951        self, name: str, entity_type: str, *, page: int = 0
 952    ) -> iterators.Iterator[entity.SearchableEntity]:
 953        """Search for Destiny2 entities given a name and its type.
 954
 955        Parameters
 956        ----------
 957        name : `str`
 958            The name of the entity, i.e., Thunderlord, One thousand voices.
 959        entity_type : `str`
 960            The type of the entity, AKA Definition,
 961            For an example `DestinyInventoryItemDefinition` for emblems, weapons, and other inventory items.
 962
 963        Other Parameters
 964        ----------------
 965        page : `int`
 966            An optional page to return. Default to 0.
 967
 968        Returns
 969        -------
 970        `aiobungie.Iterator[aiobungie.crates.SearchableEntity]`
 971            An iterator over the found results matching the provided name.
 972        """
 973        resp = await self.rest.search_entities(name, entity_type, page=page)
 974
 975        return self.factory.deserialize_inventory_results(resp)
 976
 977    # Fireteams
 978
 979    async def fetch_fireteams(
 980        self,
 981        activity_type: fireteams.FireteamActivity | int,
 982        *,
 983        platform: fireteams.FireteamPlatform | int = fireteams.FireteamPlatform.ANY,
 984        language: fireteams.FireteamLanguage | str = fireteams.FireteamLanguage.ALL,
 985        date_range: int = 0,
 986        page: int = 0,
 987        slots_filter: int = 0,
 988    ) -> collections.Sequence[fireteams.Fireteam]:
 989        """Fetch public Bungie fireteams with open slots.
 990
 991        Parameters
 992        ----------
 993        activity_type : `aiobungie.aiobungie.crates.FireteamActivity | int`
 994            The fireteam activity type.
 995
 996        Other Parameters
 997        ----------------
 998        platform : `aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int`
 999            If this is provided. Then the results will be filtered with the given platform.
1000            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1001        language : `aiobungie.crates.fireteams.FireteamLanguage | str`
1002            A locale language to filter the used language in that fireteam.
1003            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1004        date_range : `int`
1005            An integer to filter the date range of the returned fireteams. Defaults to `aiobungie.FireteamDate.ALL`.
1006        page : `int`
1007            The page number. By default its `0` which returns all available activities.
1008        slots_filter : `int`
1009            Filter the returned fireteams based on available slots. Default is `0`
1010
1011        Returns
1012        -------
1013        `collections.Sequence[fireteams.Fireteam]`
1014            A sequence of `aiobungie.crates.Fireteam`.
1015        """
1016
1017        resp = await self.rest.fetch_fireteams(
1018            activity_type,
1019            platform=platform,
1020            language=language,
1021            date_range=date_range,
1022            page=page,
1023            slots_filter=slots_filter,
1024        )
1025
1026        return self.factory.deserialize_fireteams(resp)
1027
1028    async def fetch_available_clan_fireteams(
1029        self,
1030        access_token: str,
1031        group_id: int,
1032        activity_type: fireteams.FireteamActivity | int,
1033        *,
1034        platform: fireteams.FireteamPlatform | int,
1035        language: fireteams.FireteamLanguage | str,
1036        date_range: int = 0,
1037        page: int = 0,
1038        public_only: bool = False,
1039        slots_filter: int = 0,
1040    ) -> collections.Sequence[fireteams.Fireteam]:
1041        """Fetch a clan's fireteams with open slots.
1042
1043        .. note::
1044            This method requires OAuth2: ReadGroups scope.
1045
1046        Parameters
1047        ----------
1048        access_token : `str`
1049            The bearer access token associated with the bungie account.
1050        group_id : `int`
1051            The group/clan id of the fireteam.
1052        activity_type : `aiobungie.aiobungie.crates.FireteamActivity | int`
1053            The fireteam activity type.
1054
1055        Other Parameters
1056        ----------------
1057        platform : `aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int`
1058            If this is provided. Then the results will be filtered with the given platform.
1059            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1060        language : `aiobungie.crates.fireteams.FireteamLanguage | str`
1061            A locale language to filter the used language in that fireteam.
1062            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1063        date_range : `int`
1064            An integer to filter the date range of the returned fireteams. Defaults to `0`.
1065        page : `int`
1066            The page number. By default its `0` which returns all available activities.
1067        public_only: `bool`
1068            If set to True, Then only public fireteams will be returned.
1069        slots_filter : `int`
1070            Filter the returned fireteams based on available slots. Default is `0`
1071
1072        Returns
1073        -------
1074        `collections.Sequence[aiobungie.crates.Fireteam]`
1075            A sequence of  fireteams found in the clan.
1076            `None` will be returned if nothing was found.
1077        """
1078        resp = await self.rest.fetch_available_clan_fireteams(
1079            access_token,
1080            group_id,
1081            activity_type,
1082            platform=platform,
1083            language=language,
1084            date_range=date_range,
1085            page=page,
1086            public_only=public_only,
1087            slots_filter=slots_filter,
1088        )
1089
1090        return self.factory.deserialize_fireteams(resp)
1091
1092    async def fetch_clan_fireteam(
1093        self, access_token: str, fireteam_id: int, group_id: int
1094    ) -> fireteams.AvailableFireteam:
1095        """Fetch a specific clan fireteam.
1096
1097        .. note::
1098            This method requires OAuth2: ReadGroups scope.
1099
1100        Parameters
1101        ----------
1102        access_token : `str`
1103            The bearer access token associated with the bungie account.
1104        group_id : `int`
1105            The group/clan id to fetch the fireteam from.
1106        fireteam_id : `int`
1107            The fireteam id to fetch.
1108
1109        Returns
1110        -------
1111        `aiobungie.crates.AvailableFireteam`
1112            A sequence of available fireteams objects.
1113        """
1114        resp = await self.rest.fetch_clan_fireteam(access_token, fireteam_id, group_id)
1115
1116        return self.factory.deserialize_available_fireteam(resp)
1117
1118    async def fetch_my_clan_fireteams(
1119        self,
1120        access_token: str,
1121        group_id: int,
1122        *,
1123        include_closed: bool = True,
1124        platform: fireteams.FireteamPlatform | int,
1125        language: fireteams.FireteamLanguage | str,
1126        filtered: bool = True,
1127        page: int = 0,
1128    ) -> collections.Sequence[fireteams.AvailableFireteam]:
1129        """A method that's similar to `fetch_fireteams` but requires OAuth2.
1130
1131        .. note::
1132            This method requires OAuth2: ReadGroups scope.
1133
1134        Parameters
1135        ----------
1136        access_token : str
1137            The bearer access token associated with the bungie account.
1138        group_id : int
1139            The group/clan id to fetch.
1140
1141        Other Parameters
1142        ----------------
1143        include_closed : `bool`
1144            If provided and set to True, It will also return closed fireteams.
1145            If provided and set to False, It will only return public fireteams. Default is True.
1146        platform : aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int
1147            If this is provided. Then the results will be filtered with the given platform.
1148            Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
1149        language : `aiobungie.crates.fireteams.FireteamLanguage | str`
1150            A locale language to filter the used language in that fireteam.
1151            Defaults to aiobungie.crates.FireteamLanguage.ALL
1152        filtered : `bool`
1153            If set to True, it will filter by clan. Otherwise not. Default is True.
1154        page : `int`
1155            The page number. By default its 0 which returns all available activities.
1156
1157        Returns
1158        -------
1159        `collections.Sequence[aiobungie.crates.AvailableFireteam]`
1160            A sequence of available fireteams objects if exists. else `None` will be returned.
1161        """
1162        resp = await self.rest.fetch_my_clan_fireteams(
1163            access_token,
1164            group_id,
1165            include_closed=include_closed,
1166            platform=platform,
1167            language=language,
1168            filtered=filtered,
1169            page=page,
1170        )
1171
1172        return self.factory.deserialize_available_fireteams(resp)
1173
1174    # Friends and social.
1175
1176    async def fetch_friends(
1177        self, access_token: str, /
1178    ) -> collections.Sequence[friends.Friend]:
1179        """Fetch bungie friend list.
1180
1181        .. note::
1182            This requests OAuth2: ReadUserData scope.
1183
1184        Parameters
1185        -----------
1186        access_token : `str`
1187            The bearer access token associated with the bungie account.
1188
1189        Returns
1190        -------
1191        `collections.Sequence[aiobungie.crates.Friend]`
1192            A sequence of the friends associated with that access token.
1193        """
1194
1195        resp = await self.rest.fetch_friends(access_token)
1196
1197        return self.factory.deserialize_friends(resp)
1198
1199    async def fetch_friend_requests(
1200        self, access_token: str, /
1201    ) -> friends.FriendRequestView:
1202        """Fetch pending bungie friend requests queue.
1203
1204        .. note::
1205            This requests OAuth2: ReadUserData scope.
1206
1207        Parameters
1208        -----------
1209        access_token : `str`
1210            The bearer access token associated with the bungie account.
1211
1212        Returns
1213        -------
1214        `aiobungie.crates.FriendRequestView`
1215            A friend requests view of that associated access token.
1216        """
1217
1218        resp = await self.rest.fetch_friend_requests(access_token)
1219
1220        return self.factory.deserialize_friend_requests(resp)
1221
1222    # Applications and Developer portal.
1223
1224    async def fetch_application(self, appid: int, /) -> application.Application:
1225        """Fetch a Bungie application.
1226
1227        Parameters
1228        -----------
1229        appid: `int`
1230            The application id.
1231
1232        Returns
1233        --------
1234        `aiobungie.crates.Application`
1235            A Bungie application.
1236        """
1237        resp = await self.rest.fetch_application(appid)
1238
1239        return self.factory.deserialize_app(resp)
1240
1241    # Milestones
1242
1243    async def fetch_public_milestone_content(
1244        self, milestone_hash: int, /
1245    ) -> milestones.MilestoneContent:
1246        """Fetch the milestone content given its hash.
1247
1248        Parameters
1249        ----------
1250        milestone_hash : `int`
1251            The milestone hash.
1252
1253        Returns
1254        -------
1255        `aiobungie.crates.milestones.MilestoneContent`
1256            A milestone content object.
1257        """
1258        resp = await self.rest.fetch_public_milestone_content(milestone_hash)
1259
1260        return self.factory.deserialize_public_milestone_content(resp)

Standard Bungie API client application.

This client deserialize the REST JSON responses using Factory and returns aiobungie.crates Python object implementations of the responses.

A aiobungie.RESTClient REST client can also be used alone for low-level concepts.

Example
import aiobungie

client = aiobungie.Client('...')

async def main():
    async with client.rest:
        user = await client.fetch_current_user_memberships('...')
        print(user)
Parameters
  • token (str): Your Bungie's API key or Token from the developer's portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • client_secret (str | None): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (int | None): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
Client( token: str, /, *, client_secret: str | None = None, client_id: int | None = None, max_retries: int = 4)
 98    def __init__(
 99        self,
100        token: str,
101        /,
102        *,
103        client_secret: str | None = None,
104        client_id: int | None = None,
105        max_retries: int = 4,
106    ) -> None:
107        self._rest = rest_.RESTClient(
108            token,
109            client_secret=client_secret,
110            client_id=client_id,
111            max_retries=max_retries,
112        )
113
114        self._factory = factory_.Factory(self)
factory: Factory

Returns the marshalling factory for the client.

rest: aiobungie.interfaces.rest.RESTInterface

Returns the REST client for the this client.

request: Client

A readonly ClientApp instance used for external requests.

metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

A mutable mapping storage for the user's needs.

def run( self, fn: collections.abc.Awaitable[typing.Any], debug: bool = False) -> None:
132    def run(self, fn: collections.Awaitable[typing.Any], debug: bool = False) -> None:
133        loop = helpers.get_or_make_loop()
134
135        try:
136            if not loop.is_running():
137                loop.set_debug(debug)
138                loop.run_until_complete(fn)
139
140        except Exception as exc:
141            raise RuntimeError(f"Failed to run {fn!s}") from exc
142
143        except KeyboardInterrupt:
144            _LOG.warn("Unexpected Keyboard interrupt. Exiting.")

Runs a coroutine function until its complete.

This is equivalent to asyncio.get_event_loop().run_until_complete(...)

Parameters
  • fn (collections.Awaitable[Any]): The async function to run.
  • debug (bool): Either to enable asyncio debug or not. Disabled by default.
Example
async def main() -> None:
    await fetch(...)

# Run the coroutine.
client.run(main())
async def fetch_current_user_memberships(self, access_token: str, /) -> aiobungie.crates.user.User:
148    async def fetch_current_user_memberships(self, access_token: str, /) -> user.User:
149        """Fetch and return a user object of the bungie net user associated with account.
150
151        .. warning::
152            This method requires OAuth2 scope and a Bearer access token.
153
154        Parameters
155        ----------
156        access_token : `str`
157            A valid Bearer access token for the authorization.
158
159        Returns
160        -------
161        `aiobungie.crates.user.User`
162            A user object includes the Destiny memberships and Bungie.net user.
163        """
164        resp = await self.rest.fetch_current_user_memberships(access_token)
165
166        return self.factory.deserialize_user(resp)

Fetch and return a user object of the bungie net user associated with account.

This method requires OAuth2 scope and a Bearer access token.

Parameters
  • access_token (str): A valid Bearer access token for the authorization.
Returns
  • aiobungie.crates.user.User: A user object includes the Destiny memberships and Bungie.net user.
async def fetch_bungie_user(self, id: int, /) -> aiobungie.crates.user.BungieUser:
168    async def fetch_bungie_user(self, id: int, /) -> user.BungieUser:
169        """Fetch a Bungie user by their BungieNet id.
170
171        .. note::
172            This returns a Bungie user membership only. Take a look at `Client.fetch_membership_from_id`
173            for other memberships.
174
175        Parameters
176        ----------
177        id: `int`
178            The user id.
179
180        Returns
181        -------
182        `aiobungie.crates.user.BungieUser`
183            A Bungie user.
184
185        Raises
186        ------
187        `aiobungie.error.NotFound`
188            The user was not found.
189        """
190        payload = await self.rest.fetch_bungie_user(id)
191
192        return self.factory.deserialize_bungie_user(payload)

Fetch a Bungie user by their BungieNet id.

This returns a Bungie user membership only. Take a look at Client.fetch_membership_from_id for other memberships.

Parameters
  • id (int): The user id.
Returns
  • aiobungie.crates.user.BungieUser: A Bungie user.
Raises
async def search_users( self, name: str, /) -> Iterator[aiobungie.crates.user.SearchableDestinyUser]:
194    async def search_users(
195        self, name: str, /
196    ) -> iterators.Iterator[user.SearchableDestinyUser]:
197        """Search for players and return all players that matches the same name.
198
199        Parameters
200        ----------
201        name : `buildins.str`
202            The user name.
203
204        Returns
205        -------
206        `aiobungie.Iterator[aiobungie.crates.DestinyMembership]`
207            A sequence of destiny memberships.
208        """
209        payload = await self.rest.search_users(name)
210
211        return iterators.Iterator(
212            [
213                self.factory.deserialize_searched_user(user)
214                for user in payload["searchResults"]
215            ]
216        )

Search for players and return all players that matches the same name.

Parameters
  • name (buildins.str): The user name.
Returns
async def fetch_user_themes(self) -> collections.abc.Sequence[aiobungie.crates.user.UserThemes]:
218    async def fetch_user_themes(self) -> collections.Sequence[user.UserThemes]:
219        """Fetch all available user themes.
220
221        Returns
222        -------
223        `collections.Sequence[aiobungie.crates.user.UserThemes]`
224            A sequence of user themes.
225        """
226        data = await self.rest.fetch_user_themes()
227
228        return self.factory.deserialize_user_themes(data)

Fetch all available user themes.

Returns
  • collections.Sequence[aiobungie.crates.user.UserThemes]: A sequence of user themes.
async def fetch_hard_types( self, credential: int, type: CredentialType | int = <CredentialType.STEAMID: 12>, /) -> aiobungie.crates.user.HardLinkedMembership:
230    async def fetch_hard_types(
231        self,
232        credential: int,
233        type: enums.CredentialType | int = enums.CredentialType.STEAMID,
234        /,
235    ) -> user.HardLinkedMembership:
236        """Gets any hard linked membership given a credential.
237        Only works for credentials that are public just `aiobungie.CredentialType.STEAMID` right now.
238        Cross Save aware.
239
240        Parameters
241        ----------
242        credential: `int`
243            A valid SteamID64
244        type: `aiobungie.CredentialType`
245            The credential type. This must not be changed
246            Since its only credential that works "currently"
247
248        Returns
249        -------
250        `aiobungie.crates.user.HardLinkedMembership`
251            Information about the hard linked data.
252        """
253
254        payload = await self.rest.fetch_hardlinked_credentials(credential, type)
255
256        return user.HardLinkedMembership(
257            id=int(payload["membershipId"]),
258            type=enums.MembershipType(payload["membershipType"]),
259            cross_save_type=enums.MembershipType(payload["CrossSaveOverriddenType"]),
260        )

Gets any hard linked membership given a credential. Only works for credentials that are public just aiobungie.CredentialType.STEAMID right now. Cross Save aware.

Parameters
  • credential (int): A valid SteamID64
  • type (aiobungie.CredentialType): The credential type. This must not be changed Since its only credential that works "currently"
Returns
  • aiobungie.crates.user.HardLinkedMembership: Information about the hard linked data.
async def fetch_membership_from_id( self, id: int, /, type: MembershipType | int = <MembershipType.NONE: 0>) -> aiobungie.crates.user.User:
262    async def fetch_membership_from_id(
263        self,
264        id: int,
265        /,
266        type: enums.MembershipType | int = enums.MembershipType.NONE,
267    ) -> user.User:
268        """Fetch Bungie user's memberships from their id.
269
270        Notes
271        -----
272        * This returns both BungieNet membership and a sequence of the player's DestinyMemberships
273        Which includes Stadia, Xbox, Steam and PSN memberships if the player has them,
274        see `aiobungie.crates.user.DestinyMembership` for more details.
275        * If you only want the bungie user. Consider using `Client.fetch_user` method.
276
277        Parameters
278        ----------
279        id : `int`
280            The user's id.
281        type : `aiobungie.MembershipType`
282            The user's membership type.
283
284        Returns
285        -------
286        `aiobungie.crates.User`
287            A Bungie user with their membership types.
288
289        Raises
290        ------
291        aiobungie.NotFound
292            The requested user was not found.
293        """
294        payload = await self.rest.fetch_membership_from_id(id, type)
295
296        return self.factory.deserialize_user(payload)

Fetch Bungie user's memberships from their id.

Notes
  • This returns both BungieNet membership and a sequence of the player's DestinyMemberships Which includes Stadia, Xbox, Steam and PSN memberships if the player has them, see aiobungie.crates.user.DestinyMembership for more details.
  • If you only want the bungie user. Consider using Client.fetch_user method.
Parameters
Returns
Raises
async def fetch_user_credentials( self, access_token: str, membership_id: int, /) -> collections.abc.Sequence[aiobungie.crates.user.UserCredentials]:
298    async def fetch_user_credentials(
299        self, access_token: str, membership_id: int, /
300    ) -> collections.Sequence[user.UserCredentials]:
301        """Fetch an array of credential types attached to the requested account.
302
303        .. note::
304            This method require OAuth2 Bearer access token.
305
306        Parameters
307        ----------
308        access_token : `str`
309            The bearer access token associated with the bungie account.
310        membership_id : `int`
311            The id of the membership to return.
312
313        Returns
314        -------
315        `collections.Sequence[aiobungie.crates.UserCredentials]`
316            A sequence of the attached user credentials.
317
318        Raises
319        ------
320        `aiobungie.Unauthorized`
321            The access token was wrong or no access token passed.
322        """
323        resp = await self.rest.fetch_user_credentials(access_token, membership_id)
324
325        return self.factory.deserialize_user_credentials(resp)

Fetch an array of credential types attached to the requested account.

This method require OAuth2 Bearer access token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • membership_id (int): The id of the membership to return.
Returns
Raises
async def fetch_profile( self, member_id: int, type: MembershipType | int, components: list[ComponentType], auth: str | None = None) -> aiobungie.crates.components.Component:
329    async def fetch_profile(
330        self,
331        member_id: int,
332        type: enums.MembershipType | int,
333        components: list[enums.ComponentType],
334        auth: str | None = None,
335    ) -> components.Component:
336        """
337        Fetch a bungie profile passing components to the request.
338
339        Parameters
340        ----------
341        member_id: `int`
342            The member's id.
343        type: `aiobungie.MembershipType`
344            A valid membership type.
345        components : `list[aiobungie.ComponentType]`
346            List of profile components to collect and return.
347
348        Other Parameters
349        ----------------
350        auth : `str | None`
351            A Bearer access_token to make the request with.
352            This is optional and limited to components that only requires an Authorization token.
353
354        Returns
355        --------
356        `aiobungie.crates.Component`
357            A Destiny 2 player profile with its components.
358            Only passed components will be available if they exists. Otherwise they will be `None`
359
360        Raises
361        ------
362        `aiobungie.MembershipTypeError`
363            The provided membership type was invalid.
364        """
365        data = await self.rest.fetch_profile(member_id, type, components, auth)
366        return self.factory.deserialize_components(data)

Fetch a bungie profile passing components to the request.

Parameters
Other Parameters
  • auth (str | None): A Bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
  • aiobungie.crates.Component: A Destiny 2 player profile with its components. Only passed components will be available if they exists. Otherwise they will be None
Raises
async def fetch_linked_profiles( self, member_id: int, member_type: MembershipType | int, /, *, all: bool = False) -> aiobungie.crates.profile.LinkedProfile:
368    async def fetch_linked_profiles(
369        self,
370        member_id: int,
371        member_type: enums.MembershipType | int,
372        /,
373        *,
374        all: bool = False,
375    ) -> profile.LinkedProfile:
376        """Returns a summary information about all profiles linked to the requested member.
377
378        The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.
379
380        .. note::
381            It will only return linked accounts whose linkages you are allowed to view.
382
383        Parameters
384        ----------
385        member_id : `int`
386            The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
387        member_type : `aiobungie.MembershipType`
388            The type for the membership whose linked Destiny account you want to return.
389
390        Other Parameters
391        ----------------
392        all : `bool`
393            If provided and set to `True`, All memberships regardless
394            of whether they're obscured by overrides will be returned,
395
396            If provided and set to `False`, Only available memberships will be returned.
397            The default for this is `False`.
398
399        Returns
400        -------
401        `aiobungie.crates.profile.LinkedProfile`
402            A linked profile object.
403        """
404        resp = await self.rest.fetch_linked_profiles(member_id, member_type, all=all)
405
406        return self.factory.deserialize_linked_profiles(resp)

Returns a summary information about all profiles linked to the requested member.

The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.

It will only return linked accounts whose linkages you are allowed to view.

Parameters
  • member_id (int): The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
  • member_type (aiobungie.MembershipType): The type for the membership whose linked Destiny account you want to return.
Other Parameters
  • all (bool): If provided and set to True, All memberships regardless of whether they're obscured by overrides will be returned,

    If provided and set to False, Only available memberships will be returned. The default for this is False.

Returns
  • aiobungie.crates.profile.LinkedProfile: A linked profile object.
async def fetch_membership( self, name: str, code: int, /, type: MembershipType | int = <MembershipType.ALL: -1>) -> collections.abc.Sequence[aiobungie.crates.user.DestinyMembership]:
408    async def fetch_membership(
409        self,
410        name: str,
411        code: int,
412        /,
413        type: enums.MembershipType | int = enums.MembershipType.ALL,
414    ) -> collections.Sequence[user.DestinyMembership]:
415        """Fetch a Destiny 2 player's memberships.
416
417        Parameters
418        -----------
419        name: `str`
420            The unique Bungie player name.
421        code : `int`
422            The unique Bungie display name code.
423        type: `aiobungie.internal.enums.MembershipType`
424            The player's membership type, e,g. XBOX, STEAM, PSN
425
426        Returns
427        --------
428        `collections.Sequence[aiobungie.crates.DestinyMembership]`
429            A sequence of the found Destiny 2 player memberships.
430            An empty sequence will be returned if no one found.
431
432        Raises
433        ------
434        `aiobungie.MembershipTypeError`
435            The provided membership type was invalid.
436        """
437        resp = await self.rest.fetch_membership(name, code, type)
438
439        return self.factory.deserialize_destiny_memberships(resp)

Fetch a Destiny 2 player's memberships.

Parameters
  • name (str): The unique Bungie player name.
  • code (int): The unique Bungie display name code.
  • type (MembershipType): The player's membership type, e,g. XBOX, STEAM, PSN
Returns
Raises
async def fetch_character( self, member_id: int, membership_type: MembershipType | int, character_id: int, components: list[ComponentType], auth: str | None = None) -> aiobungie.crates.components.CharacterComponent:
441    async def fetch_character(
442        self,
443        member_id: int,
444        membership_type: enums.MembershipType | int,
445        character_id: int,
446        components: list[enums.ComponentType],
447        auth: str | None = None,
448    ) -> components.CharacterComponent:
449        """Fetch a Destiny 2 character.
450
451        Parameters
452        ----------
453        member_id: `int`
454            A valid bungie member id.
455        character_id: `int`
456            The Destiny character id to retrieve.
457        membership_type: `aiobungie.internal.enums.MembershipType`
458            The member's membership type.
459        components: `list[aiobungie.ComponentType]`
460            Multiple arguments of character components to collect and return.
461
462        Other Parameters
463        ----------------
464        auth : `str | None`
465            A Bearer access_token to make the request with.
466            This is optional and limited to components that only requires an Authorization token.
467
468        Returns
469        -------
470        `aiobungie.crates.CharacterComponent`
471            A Bungie character component.
472
473        `aiobungie.MembershipTypeError`
474            The provided membership type was invalid.
475        """
476        resp = await self.rest.fetch_character(
477            member_id, membership_type, character_id, components, auth
478        )
479
480        return self.factory.deserialize_character_component(resp)

Fetch a Destiny 2 character.

Parameters
  • member_id (int): A valid bungie member id.
  • character_id (int): The Destiny character id to retrieve.
  • membership_type (MembershipType): The member's membership type.
  • components (list[aiobungie.ComponentType]): Multiple arguments of character components to collect and return.
Other Parameters
  • auth (str | None): A Bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
async def fetch_unique_weapon_history( self, membership_id: int, character_id: int, membership_type: MembershipType | int) -> collections.abc.Sequence[aiobungie.crates.activity.ExtendedWeaponValues]:
482    async def fetch_unique_weapon_history(
483        self,
484        membership_id: int,
485        character_id: int,
486        membership_type: enums.MembershipType | int,
487    ) -> collections.Sequence[activity.ExtendedWeaponValues]:
488        """Fetch details about unique weapon usage for a character. Includes all exotics.
489
490        Parameters
491        ----------
492        membership_id : `int`
493            The Destiny user membership id.
494        character_id : `int`
495            The character id to retrieve.
496        membership_type : `aiobungie.aiobungie.MembershipType | int`
497            The Destiny user's membership type.
498
499        Returns
500        -------
501        `collections.Sequence[aiobungie.crates.ExtendedWeaponValues]`
502            A sequence of the weapon's extended values.
503        """
504        resp = await self._rest.fetch_unique_weapon_history(
505            membership_id, character_id, membership_type
506        )
507
508        return [
509            self._factory.deserialize_extended_weapon_values(weapon)
510            for weapon in resp["weapons"]
511        ]

Fetch details about unique weapon usage for a character. Includes all exotics.

Parameters
  • membership_id (int): The Destiny user membership id.
  • character_id (int): The character id to retrieve.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The Destiny user's membership type.
Returns
async def fetch_activities( self, member_id: int, character_id: int, mode: GameMode | int, *, membership_type: MembershipType | int = <MembershipType.ALL: -1>, page: int = 0, limit: int = 250) -> Iterator[aiobungie.crates.activity.Activity]:
515    async def fetch_activities(
516        self,
517        member_id: int,
518        character_id: int,
519        mode: enums.GameMode | int,
520        *,
521        membership_type: enums.MembershipType | int = enums.MembershipType.ALL,
522        page: int = 0,
523        limit: int = 250,
524    ) -> iterators.Iterator[activity.Activity]:
525        """Fetch a Destiny 2 activity for the specified character id.
526
527        Parameters
528        ----------
529        member_id: `int`
530            The user id that starts with `4611`.
531        character_id: `int`
532            The id of the character to retrieve the activities for.
533        mode: `aiobungie.aiobungie.internal.enums.GameMode | int`
534            This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
535
536        Other Parameters
537        ----------------
538        membership_type: `aiobungie.internal.enums.MembershipType`
539            The Member ship type, if nothing was passed than it will return all.
540        page: int
541            The page number. Default is `0`
542        limit: int
543            Limit the returned result. Default is `250`.
544
545        Returns
546        -------
547        `aiobungie.Iterator[aiobungie.crates.Activity]`
548            An iterator of the player's activities.
549
550        Raises
551        ------
552        `aiobungie.MembershipTypeError`
553            The provided membership type was invalid.
554        """
555        resp = await self.rest.fetch_activities(
556            member_id,
557            character_id,
558            mode,
559            membership_type=membership_type,
560            page=page,
561            limit=limit,
562        )
563
564        return self.factory.deserialize_activities(resp)

Fetch a Destiny 2 activity for the specified character id.

Parameters
  • member_id (int): The user id that starts with 4611.
  • character_id (int): The id of the character to retrieve the activities for.
  • mode (aiobungie.aiobungie.internal.enums.GameMode | int): This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
Other Parameters
  • membership_type (MembershipType): The Member ship type, if nothing was passed than it will return all.
  • page (int): The page number. Default is 0
  • limit (int): Limit the returned result. Default is 250.
Returns
Raises
async def fetch_post_activity(self, instance_id: int, /) -> aiobungie.crates.activity.PostActivity:
566    async def fetch_post_activity(self, instance_id: int, /) -> activity.PostActivity:
567        """Fetch a post activity details.
568
569        Parameters
570        ----------
571        instance_id: `int`
572            The activity instance id.
573
574        Returns
575        -------
576        `aiobungie.crates.PostActivity`
577           A post activity object.
578        """
579        resp = await self.rest.fetch_post_activity(instance_id)
580
581        return self.factory.deserialize_post_activity(resp)

Fetch a post activity details.

Parameters
  • instance_id (int): The activity instance id.
Returns
async def fetch_aggregated_activity_stats( self, character_id: int, membership_id: int, membership_type: MembershipType | int) -> Iterator[aiobungie.crates.activity.AggregatedActivity]:
583    async def fetch_aggregated_activity_stats(
584        self,
585        character_id: int,
586        membership_id: int,
587        membership_type: enums.MembershipType | int,
588    ) -> iterators.Iterator[activity.AggregatedActivity]:
589        """Fetch aggregated activity stats for a character.
590
591        Parameters
592        ----------
593        character_id: `int`
594            The id of the character to retrieve the activities for.
595        membership_id: `int`
596            The id of the user that started with `4611`.
597        membership_type: `aiobungie.internal.enums.MembershipType`
598            The Member ship type.
599
600        Returns
601        -------
602        `aiobungie.Iterator[aiobungie.crates.AggregatedActivity]`
603            An iterator of the player's activities.
604
605        Raises
606        ------
607        `aiobungie.MembershipTypeError`
608            The provided membership type was invalid.
609        """
610        resp = await self.rest.fetch_aggregated_activity_stats(
611            character_id, membership_id, membership_type
612        )
613
614        return self.factory.deserialize_aggregated_activities(resp)

Fetch aggregated activity stats for a character.

Parameters
  • character_id (int): The id of the character to retrieve the activities for.
  • membership_id (int): The id of the user that started with 4611.
  • membership_type (MembershipType): The Member ship type.
Returns
Raises
async def fetch_clan_from_id( self, id: int, /, access_token: str | None = None) -> aiobungie.crates.clans.Clan:
618    async def fetch_clan_from_id(
619        self,
620        id: int,
621        /,
622        access_token: str | None = None,
623    ) -> clans.Clan:
624        """Fetch a Bungie Clan by its id.
625
626        Parameters
627        -----------
628        id: `int`
629            The clan id.
630
631        Returns
632        --------
633        `aiobungie.crates.Clan`
634            An Bungie clan.
635
636        Raises
637        ------
638        `aiobungie.NotFound`
639            The clan was not found.
640        """
641        resp = await self.rest.fetch_clan_from_id(id, access_token)
642
643        return self.factory.deserialize_clan(resp)

Fetch a Bungie Clan by its id.

Parameters
  • id (int): The clan id.
Returns
Raises
async def fetch_clan( self, name: str, /, access_token: str | None = None, *, type: GroupType | int = <GroupType.CLAN: 1>) -> aiobungie.crates.clans.Clan:
645    async def fetch_clan(
646        self,
647        name: str,
648        /,
649        access_token: str | None = None,
650        *,
651        type: enums.GroupType | int = enums.GroupType.CLAN,
652    ) -> clans.Clan:
653        """Fetch a Clan by its name.
654        This method will return the first clan found with given name.
655
656        Parameters
657        ----------
658        name: `str`
659            The clan name
660
661        Other Parameters
662        ----------------
663        access_token : `str | None`
664            An optional access token to make the request with.
665
666            If the token was bound to a member of the clan,
667            This field `aiobungie.crates.Clan.current_user_membership` will be available
668            and will return the membership of the user who made this request.
669        type : `aiobungie.GroupType`
670            The group type, Default is aiobungie.GroupType.CLAN.
671
672        Returns
673        -------
674        `aiobungie.crates.Clan`
675            A Bungie clan.
676
677        Raises
678        ------
679        `aiobungie.NotFound`
680            The clan was not found.
681        """
682        resp = await self.rest.fetch_clan(name, access_token, type=type)
683
684        return self.factory.deserialize_clan(resp)

Fetch a Clan by its name. This method will return the first clan found with given name.

Parameters
  • name (str): The clan name
Other Parameters
Returns
Raises
async def fetch_clan_conversations( self, clan_id: int, /) -> collections.abc.Sequence[aiobungie.crates.clans.ClanConversation]:
686    async def fetch_clan_conversations(
687        self, clan_id: int, /
688    ) -> collections.Sequence[clans.ClanConversation]:
689        """Fetch the conversations/chat channels of the given clan id.
690
691        Parameters
692        ----------
693        clan_id : `int`
694            The clan id.
695
696        Returns
697        `collections.Sequence[aiobungie.crates.ClanConversation]`
698            A sequence of the clan chat channels.
699        """
700        resp = await self.rest.fetch_clan_conversations(clan_id)
701
702        return self.factory.deserialize_clan_conversations(resp)

Fetch the conversations/chat channels of the given clan id.

Parameters
async def fetch_clan_admins( self, clan_id: int, /) -> Iterator[aiobungie.crates.clans.ClanMember]:
704    async def fetch_clan_admins(
705        self, clan_id: int, /
706    ) -> iterators.Iterator[clans.ClanMember]:
707        """Fetch the clan founder and admins.
708
709        Parameters
710        ----------
711        clan_id : `int`
712            The clan id.
713
714        Returns
715        -------
716        `aiobungie.Iterator[aiobungie.crates.ClanMember]`
717            An iterator over the found clan admins and founder.
718
719        Raises
720        ------
721        `aiobungie.NotFound`
722            The requested clan was not found.
723        """
724        resp = await self.rest.fetch_clan_admins(clan_id)
725
726        return self.factory.deserialize_clan_members(resp)

Fetch the clan founder and admins.

Parameters
  • clan_id (int): The clan id.
Returns
Raises
async def fetch_groups_for_member( self, member_id: int, member_type: MembershipType | int, /, *, filter: int = 0, group_type: GroupType = <GroupType.CLAN: 1>) -> collections.abc.Sequence[aiobungie.crates.clans.GroupMember]:
728    async def fetch_groups_for_member(
729        self,
730        member_id: int,
731        member_type: enums.MembershipType | int,
732        /,
733        *,
734        filter: int = 0,
735        group_type: enums.GroupType = enums.GroupType.CLAN,
736    ) -> collections.Sequence[clans.GroupMember]:
737        """Fetch information about the groups that a given member has joined.
738
739        Parameters
740        ----------
741        member_id : `int`
742            The member's id
743        member_type : `aiobungie.MembershipType`
744            The member's membership type.
745
746        Other Parameters
747        ----------------
748        filter : `int`
749            Filter apply to list of joined groups. This Default to `0`
750        group_type : `aiobungie.GroupType`
751            The group's type.
752            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
753
754        Returns
755        -------
756        `collections.Sequence[aiobungie.crates.GroupMember]`
757            A sequence of joined groups for the fetched member.
758        """
759        resp = await self.rest.fetch_groups_for_member(
760            member_id, member_type, filter=filter, group_type=group_type
761        )
762
763        return [
764            self.factory.deserialize_group_member(group) for group in resp["results"]
765        ]

Fetch information about the groups that a given member has joined.

Parameters
Other Parameters
Returns
async def fetch_potential_groups_for_member( self, member_id: int, member_type: MembershipType | int, /, *, filter: int = 0, group_type: GroupType | int = <GroupType.CLAN: 1>) -> collections.abc.Sequence[aiobungie.crates.clans.GroupMember]:
767    async def fetch_potential_groups_for_member(
768        self,
769        member_id: int,
770        member_type: enums.MembershipType | int,
771        /,
772        *,
773        filter: int = 0,
774        group_type: enums.GroupType | int = enums.GroupType.CLAN,
775    ) -> collections.Sequence[clans.GroupMember]:
776        """Fetch the potential groups for a clan member.
777
778        Parameters
779        ----------
780        member_id : `int`
781            The member's id
782        member_type : `aiobungie.aiobungie.MembershipType | int`
783            The member's membership type.
784
785        Other Parameters
786        ----------------
787        filter : `int`
788            Filter apply to list of joined groups. This Default to `0`
789        group_type : `aiobungie.aiobungie.GroupType | int`
790            The group's type.
791            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
792
793        Returns
794        -------
795        `collections.Sequence[aiobungie.crates.GroupMember]`
796            A sequence of joined potential groups for the fetched member.
797        """
798        resp = await self.rest.fetch_potential_groups_for_member(
799            member_id, member_type, filter=filter, group_type=group_type
800        )
801
802        return [
803            self.factory.deserialize_group_member(group) for group in resp["results"]
804        ]

Fetch the potential groups for a clan member.

Parameters
  • member_id (int): The member's id
  • member_type (aiobungie.aiobungie.MembershipType | int): The member's membership type.
Other Parameters
  • filter (int): Filter apply to list of joined groups. This Default to 0
  • group_type (aiobungie.aiobungie.GroupType | int): The group's type. This is always set to aiobungie.GroupType.CLAN and should not be changed.
Returns
async def fetch_clan_members( self, clan_id: int, /, *, name: str | None = None, type: MembershipType | int = <MembershipType.NONE: 0>) -> Iterator[aiobungie.crates.clans.ClanMember]:
806    async def fetch_clan_members(
807        self,
808        clan_id: int,
809        /,
810        *,
811        name: str | None = None,
812        type: enums.MembershipType | int = enums.MembershipType.NONE,
813    ) -> iterators.Iterator[clans.ClanMember]:
814        """Fetch Bungie clan members.
815
816        Parameters
817        ----------
818        clan_id : `int`
819            The clans id
820
821        Other Parameters
822        ----------------
823        name : `str | None`
824            If provided, Only players matching this name will be returned.
825        type : `aiobungie.MembershipType`
826            An optional clan member's membership type.
827            This parameter is used to filter the returned results
828            by the provided membership, For an example XBox memberships only,
829            Otherwise will return all memberships.
830
831        Returns
832        -------
833        `aiobungie.Iterator[aiobungie.crates.ClanMember]`
834            An iterator over the bungie clan members.
835
836        Raises
837        ------
838        `aiobungie.NotFound`
839            The clan was not found.
840        """
841        resp = await self.rest.fetch_clan_members(clan_id, type=type, name=name)
842
843        return self.factory.deserialize_clan_members(resp)

Fetch Bungie clan members.

Parameters
  • clan_id (int): The clans id
Other Parameters
  • name (str | None): If provided, Only players matching this name will be returned.
  • type (aiobungie.MembershipType): An optional clan member's membership type. This parameter is used to filter the returned results by the provided membership, For an example XBox memberships only, Otherwise will return all memberships.
Returns
Raises
async def fetch_clan_banners(self) -> collections.abc.Sequence[aiobungie.crates.clans.ClanBanner]:
845    async def fetch_clan_banners(self) -> collections.Sequence[clans.ClanBanner]:
846        """Fetch the clan banners.
847
848        Returns
849        -------
850        `collections.Sequence[aiobungie.crates.ClanBanner]`
851            A sequence of the clan banners.
852        """
853        resp = await self.rest.fetch_clan_banners()
854
855        return self.factory.deserialize_clan_banners(resp)

Fetch the clan banners.

Returns
async def kick_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: MembershipType | int) -> aiobungie.crates.clans.Clan:
858    async def kick_clan_member(
859        self,
860        access_token: str,
861        /,
862        group_id: int,
863        membership_id: int,
864        membership_type: enums.MembershipType | int,
865    ) -> clans.Clan:
866        """Kick a member from the clan.
867
868        .. note::
869            This request requires OAuth2: oauth2: `AdminGroups` scope.
870
871        Parameters
872        ----------
873        access_token : `str`
874            The bearer access token associated with the bungie account.
875        group_id: `int`
876            The group id.
877        membership_id : `int`
878            The member id to kick.
879        membership_type : `aiobungie.aiobungie.MembershipType | int`
880            The member's membership type.
881
882        Returns
883        -------
884        `aiobungie.crates.clan.Clan`
885            The clan that the member was kicked from.
886        """
887        resp = await self.rest.kick_clan_member(
888            access_token,
889            group_id=group_id,
890            membership_id=membership_id,
891            membership_type=membership_type,
892        )
893
894        return self.factory.deserialize_clan(resp)

Kick a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to kick.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The member's membership type.
Returns
  • aiobungie.crates.clan.Clan: The clan that the member was kicked from.
async def fetch_clan_weekly_rewards(self, clan_id: int) -> aiobungie.crates.milestones.Milestone:
896    async def fetch_clan_weekly_rewards(self, clan_id: int) -> milestones.Milestone:
897        """Fetch a Bungie clan's weekly reward state.
898
899        Parameters
900        ----------
901        clan_id : `int`
902            The clan's id.
903
904        Returns
905        -------
906        `aiobungie.crates.Milestone`
907            A runtime status of the clan's milestone data.
908        """
909
910        resp = await self.rest.fetch_clan_weekly_rewards(clan_id)
911
912        return self.factory.deserialize_milestone(resp)

Fetch a Bungie clan's weekly reward state.

Parameters
  • clan_id (int): The clan's id.
Returns
async def fetch_inventory_item(self, hash: int, /) -> aiobungie.crates.entity.InventoryEntity:
916    async def fetch_inventory_item(self, hash: int, /) -> entity.InventoryEntity:
917        """Fetch a static inventory item entity given a its hash.
918
919        Parameters
920        ----------
921        hash: `int`
922            Inventory item's hash.
923
924        Returns
925        -------
926        `aiobungie.crates.InventoryEntity`
927            A bungie inventory item.
928        """
929        resp = await self.rest.fetch_inventory_item(hash)
930
931        return self.factory.deserialize_inventory_entity(resp)

Fetch a static inventory item entity given a its hash.

Parameters
  • hash (int): Inventory item's hash.
Returns
async def fetch_objective_entity(self, hash: int, /) -> aiobungie.crates.entity.ObjectiveEntity:
933    async def fetch_objective_entity(self, hash: int, /) -> entity.ObjectiveEntity:
934        """Fetch a Destiny objective entity given a its hash.
935
936        Parameters
937        ----------
938        hash: `int`
939            objective's hash.
940
941        Returns
942        -------
943        `aiobungie.crates.ObjectiveEntity`
944            An objective entity item.
945        """
946        resp = await self.rest.fetch_objective_entity(hash)
947
948        return self.factory.deserialize_objective_entity(resp)

Fetch a Destiny objective entity given a its hash.

Parameters
  • hash (int): objective's hash.
Returns
async def search_entities( self, name: str, entity_type: str, *, page: int = 0) -> Iterator[aiobungie.crates.entity.SearchableEntity]:
950    async def search_entities(
951        self, name: str, entity_type: str, *, page: int = 0
952    ) -> iterators.Iterator[entity.SearchableEntity]:
953        """Search for Destiny2 entities given a name and its type.
954
955        Parameters
956        ----------
957        name : `str`
958            The name of the entity, i.e., Thunderlord, One thousand voices.
959        entity_type : `str`
960            The type of the entity, AKA Definition,
961            For an example `DestinyInventoryItemDefinition` for emblems, weapons, and other inventory items.
962
963        Other Parameters
964        ----------------
965        page : `int`
966            An optional page to return. Default to 0.
967
968        Returns
969        -------
970        `aiobungie.Iterator[aiobungie.crates.SearchableEntity]`
971            An iterator over the found results matching the provided name.
972        """
973        resp = await self.rest.search_entities(name, entity_type, page=page)
974
975        return self.factory.deserialize_inventory_results(resp)

Search for Destiny2 entities given a name and its type.

Parameters
  • name (str): The name of the entity, i.e., Thunderlord, One thousand voices.
  • entity_type (str): The type of the entity, AKA Definition, For an example DestinyInventoryItemDefinition for emblems, weapons, and other inventory items.
Other Parameters
  • page (int): An optional page to return. Default to 0.
Returns
async def fetch_fireteams( self, activity_type: FireteamActivity | int, *, platform: FireteamPlatform | int = <FireteamPlatform.ANY: 0>, language: FireteamLanguage | str = <FireteamLanguage.ALL: >, date_range: int = 0, page: int = 0, slots_filter: int = 0) -> collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]:
 979    async def fetch_fireteams(
 980        self,
 981        activity_type: fireteams.FireteamActivity | int,
 982        *,
 983        platform: fireteams.FireteamPlatform | int = fireteams.FireteamPlatform.ANY,
 984        language: fireteams.FireteamLanguage | str = fireteams.FireteamLanguage.ALL,
 985        date_range: int = 0,
 986        page: int = 0,
 987        slots_filter: int = 0,
 988    ) -> collections.Sequence[fireteams.Fireteam]:
 989        """Fetch public Bungie fireteams with open slots.
 990
 991        Parameters
 992        ----------
 993        activity_type : `aiobungie.aiobungie.crates.FireteamActivity | int`
 994            The fireteam activity type.
 995
 996        Other Parameters
 997        ----------------
 998        platform : `aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int`
 999            If this is provided. Then the results will be filtered with the given platform.
1000            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1001        language : `aiobungie.crates.fireteams.FireteamLanguage | str`
1002            A locale language to filter the used language in that fireteam.
1003            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1004        date_range : `int`
1005            An integer to filter the date range of the returned fireteams. Defaults to `aiobungie.FireteamDate.ALL`.
1006        page : `int`
1007            The page number. By default its `0` which returns all available activities.
1008        slots_filter : `int`
1009            Filter the returned fireteams based on available slots. Default is `0`
1010
1011        Returns
1012        -------
1013        `collections.Sequence[fireteams.Fireteam]`
1014            A sequence of `aiobungie.crates.Fireteam`.
1015        """
1016
1017        resp = await self.rest.fetch_fireteams(
1018            activity_type,
1019            platform=platform,
1020            language=language,
1021            date_range=date_range,
1022            page=page,
1023            slots_filter=slots_filter,
1024        )
1025
1026        return self.factory.deserialize_fireteams(resp)

Fetch public Bungie fireteams with open slots.

Parameters
  • activity_type (aiobungie.aiobungie.crates.FireteamActivity | int): The fireteam activity type.
Other Parameters
  • platform (aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (int): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_available_clan_fireteams( self, access_token: str, group_id: int, activity_type: FireteamActivity | int, *, platform: FireteamPlatform | int, language: FireteamLanguage | str, date_range: int = 0, page: int = 0, public_only: bool = False, slots_filter: int = 0) -> collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]:
1028    async def fetch_available_clan_fireteams(
1029        self,
1030        access_token: str,
1031        group_id: int,
1032        activity_type: fireteams.FireteamActivity | int,
1033        *,
1034        platform: fireteams.FireteamPlatform | int,
1035        language: fireteams.FireteamLanguage | str,
1036        date_range: int = 0,
1037        page: int = 0,
1038        public_only: bool = False,
1039        slots_filter: int = 0,
1040    ) -> collections.Sequence[fireteams.Fireteam]:
1041        """Fetch a clan's fireteams with open slots.
1042
1043        .. note::
1044            This method requires OAuth2: ReadGroups scope.
1045
1046        Parameters
1047        ----------
1048        access_token : `str`
1049            The bearer access token associated with the bungie account.
1050        group_id : `int`
1051            The group/clan id of the fireteam.
1052        activity_type : `aiobungie.aiobungie.crates.FireteamActivity | int`
1053            The fireteam activity type.
1054
1055        Other Parameters
1056        ----------------
1057        platform : `aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int`
1058            If this is provided. Then the results will be filtered with the given platform.
1059            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1060        language : `aiobungie.crates.fireteams.FireteamLanguage | str`
1061            A locale language to filter the used language in that fireteam.
1062            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1063        date_range : `int`
1064            An integer to filter the date range of the returned fireteams. Defaults to `0`.
1065        page : `int`
1066            The page number. By default its `0` which returns all available activities.
1067        public_only: `bool`
1068            If set to True, Then only public fireteams will be returned.
1069        slots_filter : `int`
1070            Filter the returned fireteams based on available slots. Default is `0`
1071
1072        Returns
1073        -------
1074        `collections.Sequence[aiobungie.crates.Fireteam]`
1075            A sequence of  fireteams found in the clan.
1076            `None` will be returned if nothing was found.
1077        """
1078        resp = await self.rest.fetch_available_clan_fireteams(
1079            access_token,
1080            group_id,
1081            activity_type,
1082            platform=platform,
1083            language=language,
1084            date_range=date_range,
1085            page=page,
1086            public_only=public_only,
1087            slots_filter=slots_filter,
1088        )
1089
1090        return self.factory.deserialize_fireteams(resp)

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id of the fireteam.
  • activity_type (aiobungie.aiobungie.crates.FireteamActivity | int): The fireteam activity type.
Other Parameters
  • platform (aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (int): An integer to filter the date range of the returned fireteams. Defaults to 0.
  • page (int): The page number. By default its 0 which returns all available activities.
  • public_only (bool): If set to True, Then only public fireteams will be returned.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
  • collections.Sequence[aiobungie.crates.Fireteam]: A sequence of fireteams found in the clan. None will be returned if nothing was found.
async def fetch_clan_fireteam( self, access_token: str, fireteam_id: int, group_id: int) -> aiobungie.crates.fireteams.AvailableFireteam:
1092    async def fetch_clan_fireteam(
1093        self, access_token: str, fireteam_id: int, group_id: int
1094    ) -> fireteams.AvailableFireteam:
1095        """Fetch a specific clan fireteam.
1096
1097        .. note::
1098            This method requires OAuth2: ReadGroups scope.
1099
1100        Parameters
1101        ----------
1102        access_token : `str`
1103            The bearer access token associated with the bungie account.
1104        group_id : `int`
1105            The group/clan id to fetch the fireteam from.
1106        fireteam_id : `int`
1107            The fireteam id to fetch.
1108
1109        Returns
1110        -------
1111        `aiobungie.crates.AvailableFireteam`
1112            A sequence of available fireteams objects.
1113        """
1114        resp = await self.rest.fetch_clan_fireteam(access_token, fireteam_id, group_id)
1115
1116        return self.factory.deserialize_available_fireteam(resp)

Fetch a specific clan fireteam.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch the fireteam from.
  • fireteam_id (int): The fireteam id to fetch.
Returns
async def fetch_my_clan_fireteams( self, access_token: str, group_id: int, *, include_closed: bool = True, platform: FireteamPlatform | int, language: FireteamLanguage | str, filtered: bool = True, page: int = 0) -> collections.abc.Sequence[aiobungie.crates.fireteams.AvailableFireteam]:
1118    async def fetch_my_clan_fireteams(
1119        self,
1120        access_token: str,
1121        group_id: int,
1122        *,
1123        include_closed: bool = True,
1124        platform: fireteams.FireteamPlatform | int,
1125        language: fireteams.FireteamLanguage | str,
1126        filtered: bool = True,
1127        page: int = 0,
1128    ) -> collections.Sequence[fireteams.AvailableFireteam]:
1129        """A method that's similar to `fetch_fireteams` but requires OAuth2.
1130
1131        .. note::
1132            This method requires OAuth2: ReadGroups scope.
1133
1134        Parameters
1135        ----------
1136        access_token : str
1137            The bearer access token associated with the bungie account.
1138        group_id : int
1139            The group/clan id to fetch.
1140
1141        Other Parameters
1142        ----------------
1143        include_closed : `bool`
1144            If provided and set to True, It will also return closed fireteams.
1145            If provided and set to False, It will only return public fireteams. Default is True.
1146        platform : aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int
1147            If this is provided. Then the results will be filtered with the given platform.
1148            Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
1149        language : `aiobungie.crates.fireteams.FireteamLanguage | str`
1150            A locale language to filter the used language in that fireteam.
1151            Defaults to aiobungie.crates.FireteamLanguage.ALL
1152        filtered : `bool`
1153            If set to True, it will filter by clan. Otherwise not. Default is True.
1154        page : `int`
1155            The page number. By default its 0 which returns all available activities.
1156
1157        Returns
1158        -------
1159        `collections.Sequence[aiobungie.crates.AvailableFireteam]`
1160            A sequence of available fireteams objects if exists. else `None` will be returned.
1161        """
1162        resp = await self.rest.fetch_my_clan_fireteams(
1163            access_token,
1164            group_id,
1165            include_closed=include_closed,
1166            platform=platform,
1167            language=language,
1168            filtered=filtered,
1169            page=page,
1170        )
1171
1172        return self.factory.deserialize_available_fireteams(resp)

A method that's similar to fetch_fireteams but requires OAuth2.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch.
Other Parameters
  • include_closed (bool): If provided and set to True, It will also return closed fireteams. If provided and set to False, It will only return public fireteams. Default is True.
  • platform (aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • filtered (bool): If set to True, it will filter by clan. Otherwise not. Default is True.
  • page (int): The page number. By default its 0 which returns all available activities.
Returns
async def fetch_friends( self, access_token: str, /) -> collections.abc.Sequence[aiobungie.crates.friends.Friend]:
1176    async def fetch_friends(
1177        self, access_token: str, /
1178    ) -> collections.Sequence[friends.Friend]:
1179        """Fetch bungie friend list.
1180
1181        .. note::
1182            This requests OAuth2: ReadUserData scope.
1183
1184        Parameters
1185        -----------
1186        access_token : `str`
1187            The bearer access token associated with the bungie account.
1188
1189        Returns
1190        -------
1191        `collections.Sequence[aiobungie.crates.Friend]`
1192            A sequence of the friends associated with that access token.
1193        """
1194
1195        resp = await self.rest.fetch_friends(access_token)
1196
1197        return self.factory.deserialize_friends(resp)

Fetch bungie friend list.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_friend_requests(self, access_token: str, /) -> aiobungie.crates.friends.FriendRequestView:
1199    async def fetch_friend_requests(
1200        self, access_token: str, /
1201    ) -> friends.FriendRequestView:
1202        """Fetch pending bungie friend requests queue.
1203
1204        .. note::
1205            This requests OAuth2: ReadUserData scope.
1206
1207        Parameters
1208        -----------
1209        access_token : `str`
1210            The bearer access token associated with the bungie account.
1211
1212        Returns
1213        -------
1214        `aiobungie.crates.FriendRequestView`
1215            A friend requests view of that associated access token.
1216        """
1217
1218        resp = await self.rest.fetch_friend_requests(access_token)
1219
1220        return self.factory.deserialize_friend_requests(resp)

Fetch pending bungie friend requests queue.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_application(self, appid: int, /) -> aiobungie.crates.application.Application:
1224    async def fetch_application(self, appid: int, /) -> application.Application:
1225        """Fetch a Bungie application.
1226
1227        Parameters
1228        -----------
1229        appid: `int`
1230            The application id.
1231
1232        Returns
1233        --------
1234        `aiobungie.crates.Application`
1235            A Bungie application.
1236        """
1237        resp = await self.rest.fetch_application(appid)
1238
1239        return self.factory.deserialize_app(resp)

Fetch a Bungie application.

Parameters
  • appid (int): The application id.
Returns
async def fetch_public_milestone_content( self, milestone_hash: int, /) -> aiobungie.crates.milestones.MilestoneContent:
1243    async def fetch_public_milestone_content(
1244        self, milestone_hash: int, /
1245    ) -> milestones.MilestoneContent:
1246        """Fetch the milestone content given its hash.
1247
1248        Parameters
1249        ----------
1250        milestone_hash : `int`
1251            The milestone hash.
1252
1253        Returns
1254        -------
1255        `aiobungie.crates.milestones.MilestoneContent`
1256            A milestone content object.
1257        """
1258        resp = await self.rest.fetch_public_milestone_content(milestone_hash)
1259
1260        return self.factory.deserialize_public_milestone_content(resp)

Fetch the milestone content given its hash.

Parameters
  • milestone_hash (int): The milestone hash.
Returns
  • aiobungie.crates.milestones.MilestoneContent: A milestone content object.
@typing.final
class ClosedReasons(aiobungie.Flag):
770@typing.final
771class ClosedReasons(Flag):
772    """A Flags enumeration representing the reasons why a person can't join this user's fireteam."""
773
774    NONE = 0
775    MATCHMAKING = 1 << 0
776    LOADING = 1 << 1
777    SOLO = 1 << 2
778    """The activity is required to be played solo."""
779    INTERNAL_REASONS = 1 << 3
780    """
781    The user can't be joined for one of a variety of internal reasons.
782    Basically, the game can't let you join at this time,
783    but for reasons that aren't under the control of this user
784    """
785    DISALLOWED_BY_GAME_STATE = 1 << 4
786    """The user's current activity/quest/other transitory game state is preventing joining."""
787    OFFLINE = 32768
788    """The user appears offline."""

A Flags enumeration representing the reasons why a person can't join this user's fireteam.

NONE = <ClosedReasons.NONE: 0>
MATCHMAKING = <ClosedReasons.MATCHMAKING: 1>
LOADING = <ClosedReasons.LOADING: 2>
SOLO = <ClosedReasons.SOLO: 4>

The activity is required to be played solo.

INTERNAL_REASONS = <ClosedReasons.INTERNAL_REASONS: 8>

The user can't be joined for one of a variety of internal reasons. Basically, the game can't let you join at this time, but for reasons that aren't under the control of this user

DISALLOWED_BY_GAME_STATE = <ClosedReasons.DISALLOWED_BY_GAME_STATE: 16>

The user's current activity/quest/other transitory game state is preventing joining.

OFFLINE = <ClosedReasons.OFFLINE: 32768>

The user appears offline.

Inherited Members
Flag
name
value
@typing.final
class ComponentFields(aiobungie.Enum):
74@typing.final
75class ComponentFields(enums.Enum):
76    """An enum that provides fields found in a base component response."""
77
78    PRIVACY = ComponentPrivacy.NONE
79    DISABLED = False

An enum that provides fields found in a base component response.

PRIVACY = <ComponentFields.PRIVACY: NONE>
DISABLED = <ComponentFields.PRIVACY: NONE>
Inherited Members
Enum
name
value
@typing.final
class ComponentPrivacy(builtins.int, aiobungie.Enum):
65@typing.final
66class ComponentPrivacy(int, enums.Enum):
67    """An enum the provides privacy settings for profile components."""
68
69    NONE = 0
70    PUBLIC = 1
71    PRIVATE = 2

An enum the provides privacy settings for profile components.

NONE = <ComponentPrivacy.NONE: 0>
PUBLIC = <ComponentPrivacy.PUBLIC: 1>
PRIVATE = <ComponentPrivacy.PRIVATE: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ComponentType(aiobungie.Enum):
347@typing.final
348class ComponentType(Enum):
349    """An Enum for Destiny 2 profile Components."""
350
351    NONE = 0
352
353    PROFILE = 100
354    PROFILE_INVENTORIES = 102
355    PROFILE_CURRENCIES = 103
356    PROFILE_PROGRESSION = 104
357    ALL_PROFILES = (
358        PROFILE,
359        PROFILE_INVENTORIES,
360        PROFILE_CURRENCIES,
361        PROFILE_PROGRESSION,
362    )
363    """All profile components."""
364
365    VENDORS = 400
366    VENDOR_SALES = 402
367    VENDOR_RECEIPTS = 101
368    ALL_VENDORS = (VENDORS, VENDOR_RECEIPTS, VENDOR_SALES)
369    """All vendor components."""
370
371    # Items
372    ITEM_INSTANCES = 300
373    ITEM_OBJECTIVES = 301
374    ITEM_PERKS = 302
375    ITEM_RENDER_DATA = 303
376    ITEM_STATS = 304
377    ITEM_SOCKETS = 305
378    ITEM_TALENT_GRINDS = 306
379    ITEM_PLUG_STATES = 308
380    ITEM_PLUG_OBJECTIVES = 309
381    ITEM_REUSABLE_PLUGS = 310
382
383    ALL_ITEMS = (
384        ITEM_PLUG_OBJECTIVES,
385        ITEM_PLUG_STATES,
386        ITEM_SOCKETS,
387        ITEM_INSTANCES,
388        ITEM_OBJECTIVES,
389        ITEM_PERKS,
390        ITEM_RENDER_DATA,
391        ITEM_STATS,
392        ITEM_TALENT_GRINDS,
393        ITEM_REUSABLE_PLUGS,
394    )
395    """All item components."""
396
397    PLATFORM_SILVER = 105
398    KIOSKS = 500
399    CURRENCY_LOOKUPS = 600
400    PRESENTATION_NODES = 700
401    COLLECTIBLES = 800
402    RECORDS = 900
403    TRANSITORY = 1000
404    METRICS = 1100
405    INVENTORIES = 102
406    STRING_VARIABLES = 1200
407    CRAFTABLES = 1300
408
409    CHARACTERS = 200
410    CHARACTER_INVENTORY = 201
411    CHARECTER_PROGRESSION = 202
412    CHARACTER_RENDER_DATA = 203
413    CHARACTER_ACTIVITIES = 204
414    CHARACTER_EQUIPMENT = 205
415    CHARACTER_LOADOUTS = 206
416
417    ALL_CHARACTERS = (
418        CHARACTERS,
419        CHARACTER_INVENTORY,
420        CHARECTER_PROGRESSION,
421        CHARACTER_RENDER_DATA,
422        CHARACTER_ACTIVITIES,
423        CHARACTER_EQUIPMENT,
424        CHARACTER_LOADOUTS,
425        RECORDS,
426    )
427    """All character components."""
428
429    ALL = (
430        *ALL_PROFILES,  # type: ignore
431        *ALL_CHARACTERS,  # type: ignore
432        *ALL_VENDORS,  # type: ignore
433        *ALL_ITEMS,  # type: ignore
434        RECORDS,
435        CURRENCY_LOOKUPS,
436        PRESENTATION_NODES,
437        COLLECTIBLES,
438        KIOSKS,
439        METRICS,
440        PLATFORM_SILVER,
441        INVENTORIES,
442        STRING_VARIABLES,
443        TRANSITORY,
444        CRAFTABLES,
445    )
446    """ALl components included."""

An Enum for Destiny 2 profile Components.

NONE = <ComponentType.NONE: 0>
PROFILE = <ComponentType.PROFILE: 100>
PROFILE_INVENTORIES = <ComponentType.PROFILE_INVENTORIES: 102>
PROFILE_CURRENCIES = <ComponentType.PROFILE_CURRENCIES: 103>
PROFILE_PROGRESSION = <ComponentType.PROFILE_PROGRESSION: 104>
ALL_PROFILES = <ComponentType.ALL_PROFILES: (100, 102, 103, 104)>

All profile components.

VENDORS = <ComponentType.VENDORS: 400>
VENDOR_SALES = <ComponentType.VENDOR_SALES: 402>
VENDOR_RECEIPTS = <ComponentType.VENDOR_RECEIPTS: 101>
ALL_VENDORS = <ComponentType.ALL_VENDORS: (400, 101, 402)>

All vendor components.

ITEM_INSTANCES = <ComponentType.ITEM_INSTANCES: 300>
ITEM_OBJECTIVES = <ComponentType.ITEM_OBJECTIVES: 301>
ITEM_PERKS = <ComponentType.ITEM_PERKS: 302>
ITEM_RENDER_DATA = <ComponentType.ITEM_RENDER_DATA: 303>
ITEM_STATS = <ComponentType.ITEM_STATS: 304>
ITEM_SOCKETS = <ComponentType.ITEM_SOCKETS: 305>
ITEM_TALENT_GRINDS = <ComponentType.ITEM_TALENT_GRINDS: 306>
ITEM_PLUG_STATES = <ComponentType.ITEM_PLUG_STATES: 308>
ITEM_PLUG_OBJECTIVES = <ComponentType.ITEM_PLUG_OBJECTIVES: 309>
ITEM_REUSABLE_PLUGS = <ComponentType.ITEM_REUSABLE_PLUGS: 310>
ALL_ITEMS = <ComponentType.ALL_ITEMS: (309, 308, 305, 300, 301, 302, 303, 304, 306, 310)>

All item components.

PLATFORM_SILVER = <ComponentType.PLATFORM_SILVER: 105>
KIOSKS = <ComponentType.KIOSKS: 500>
CURRENCY_LOOKUPS = <ComponentType.CURRENCY_LOOKUPS: 600>
PRESENTATION_NODES = <ComponentType.PRESENTATION_NODES: 700>
COLLECTIBLES = <ComponentType.COLLECTIBLES: 800>
RECORDS = <ComponentType.RECORDS: 900>
TRANSITORY = <ComponentType.TRANSITORY: 1000>
METRICS = <ComponentType.METRICS: 1100>
INVENTORIES = <ComponentType.PROFILE_INVENTORIES: 102>
STRING_VARIABLES = <ComponentType.STRING_VARIABLES: 1200>
CRAFTABLES = <ComponentType.CRAFTABLES: 1300>
CHARACTERS = <ComponentType.CHARACTERS: 200>
CHARACTER_INVENTORY = <ComponentType.CHARACTER_INVENTORY: 201>
CHARECTER_PROGRESSION = <ComponentType.CHARECTER_PROGRESSION: 202>
CHARACTER_RENDER_DATA = <ComponentType.CHARACTER_RENDER_DATA: 203>
CHARACTER_ACTIVITIES = <ComponentType.CHARACTER_ACTIVITIES: 204>
CHARACTER_EQUIPMENT = <ComponentType.CHARACTER_EQUIPMENT: 205>
CHARACTER_LOADOUTS = <ComponentType.CHARACTER_LOADOUTS: 206>
ALL_CHARACTERS = <ComponentType.ALL_CHARACTERS: (200, 201, 202, 203, 204, 205, 206, 900)>

All character components.

ALL = <ComponentType.ALL: (100, 102, 103, 104, 200, 201, 202, 203, 204, 205, 206, 900, 400, 101, 402, 309, 308, 305, 300, 301, 302, 303, 304, 306, 310, 900, 600, 700, 800, 500, 1100, 105, 102, 1200, 1000, 1300)>

ALl components included.

Inherited Members
Enum
name
value
@typing.final
class CredentialType(builtins.int, aiobungie.Enum):
652@typing.final
653class CredentialType(int, Enum):
654    """The types of the accounts system supports at bungie."""
655
656    NONE = 0
657    XUID = 1
658    PSNID = 2
659    WILD = 3
660    FAKE = 4
661    FACEBOOK = 5
662    GOOGLE = 8
663    WINDOWS = 9
664    DEMONID = 10
665    STEAMID = 12
666    BATTLENETID = 14
667    STADIAID = 16
668    TWITCHID = 18

The types of the accounts system supports at bungie.

NONE = <CredentialType.NONE: 0>
XUID = <CredentialType.XUID: 1>
PSNID = <CredentialType.PSNID: 2>
WILD = <CredentialType.WILD: 3>
FAKE = <CredentialType.FAKE: 4>
FACEBOOK = <CredentialType.FACEBOOK: 5>
GOOGLE = <CredentialType.GOOGLE: 8>
WINDOWS = <CredentialType.WINDOWS: 9>
DEMONID = <CredentialType.DEMONID: 10>
STEAMID = <CredentialType.STEAMID: 12>
BATTLENETID = <CredentialType.BATTLENETID: 14>
STADIAID = <CredentialType.STADIAID: 16>
TWITCHID = <CredentialType.TWITCHID: 18>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class DamageType(builtins.int, aiobungie.Enum):
530@typing.final
531class DamageType(int, Enum):
532    """Enums for Destiny Damage types"""
533
534    NONE = 0
535    KINETIC = 1
536    ARC = 2
537    SOLAR = 3
538    VOID = 4
539    RAID = 5
540    """This is a special damage type reserved for some raid activity encounters."""
541    STASIS = 6

Enums for Destiny Damage types

NONE = <DamageType.NONE: 0>
KINETIC = <DamageType.KINETIC: 1>
ARC = <DamageType.ARC: 2>
SOLAR = <DamageType.SOLAR: 3>
VOID = <DamageType.VOID: 4>
RAID = <DamageType.RAID: 5>

This is a special damage type reserved for some raid activity encounters.

STASIS = <DamageType.STASIS: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Difficulty(builtins.int, aiobungie.Enum):
63@typing.final
64class Difficulty(int, enums.Enum):
65    """An enum for activities difficulties."""
66
67    TRIVIAL = 0
68    EASY = 1
69    NORMAL = 2
70    CHALLENGING = 3
71    HARD = 4
72    BRAVE = 5
73    ALMOST_IMPOSSIBLE = 6
74    IMPOSSIBLE = 7

An enum for activities difficulties.

TRIVIAL = <Difficulty.TRIVIAL: 0>
EASY = <Difficulty.EASY: 1>
NORMAL = <Difficulty.NORMAL: 2>
CHALLENGING = <Difficulty.CHALLENGING: 3>
HARD = <Difficulty.HARD: 4>
BRAVE = <Difficulty.BRAVE: 5>
ALMOST_IMPOSSIBLE = <Difficulty.ALMOST_IMPOSSIBLE: 6>
IMPOSSIBLE = <Difficulty.IMPOSSIBLE: 7>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Dungeon(builtins.int, aiobungie.Enum):
149@typing.final
150class Dungeon(int, Enum):
151    """An Enum for all available Dungeon/Like missions in Destiny 2."""
152
153    NORMAL_PRESAGE = 2124066889
154    """Normal Presage"""
155
156    MASTER_PRESAGE = 4212753278
157    """Master Presage"""
158
159    HARBINGER = 1738383283
160    """Harbinger"""
161
162    PROPHECY = 4148187374
163    """Prophecy"""
164
165    MASTER_POH = 785700673
166    """Master Pit of Heresy?"""
167
168    LEGEND_POH = 785700678
169    """Legend Pit of Heresy?"""
170
171    POH = 1375089621
172    """Normal Pit of Heresy."""
173
174    SHATTERED = 2032534090
175    """Shattered Throne"""
176
177    GOA_LEGEND = 4078656646
178    """Grasp of Avarice legend."""
179
180    GOA_MASTER = 3774021532
181    """Grasp of Avarice master."""

An Enum for all available Dungeon/Like missions in Destiny 2.

NORMAL_PRESAGE = <Dungeon.NORMAL_PRESAGE: 2124066889>

Normal Presage

MASTER_PRESAGE = <Dungeon.MASTER_PRESAGE: 4212753278>

Master Presage

HARBINGER = <Dungeon.HARBINGER: 1738383283>

Harbinger

PROPHECY = <Dungeon.PROPHECY: 4148187374>

Prophecy

MASTER_POH = <Dungeon.MASTER_POH: 785700673>

Master Pit of Heresy?

LEGEND_POH = <Dungeon.LEGEND_POH: 785700678>

Legend Pit of Heresy?

POH = <Dungeon.POH: 1375089621>

Normal Pit of Heresy.

SHATTERED = <Dungeon.SHATTERED: 2032534090>

Shattered Throne

GOA_LEGEND = <Dungeon.GOA_LEGEND: 4078656646>

Grasp of Avarice legend.

GOA_MASTER = <Dungeon.GOA_MASTER: 3774021532>

Grasp of Avarice master.

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class EmptyFactory(aiobungie.Factory):
2414class EmptyFactory(Factory):
2415    """A stand-alone factory that doesn't requires a client instance.
2416
2417    # Example
2418    ---------
2419    ```py
2420    # We'll implement a serializable RESTClient.
2421    @dataclass(slots=True)
2422    class MyClient(aiobungie.traits.Serializable):
2423        rest = aiobungie.RESTClient(env["CLIENT_TOKEN"])
2424        my_name = "Fate怒"
2425        my_code = 4275
2426
2427        # Must implement this one method.
2428        @property
2429        def factory(self) -> aiobungie.EmptyFactory:
2430            # Return an empty factory
2431            return aiobungie.EmptyFactory()
2432
2433        async def my_memberships(self) -> Sequence[aiobungie.crates.DestinyMembership]:
2434            # Note, Do not call methods within objects, Since this is an empty
2435            # factory, The client reference that makes these calls will be `None`.
2436            response = await self.rest.fetch_membership(self.my_name, self.my_code)
2437            return self.factory.deserialize_destiny_memberships(response)
2438
2439
2440        async def main() -> None:
2441            client = MyClient()
2442            async with client.client:
2443                print(await client.my_memberships())
2444
2445    asyncio.run(main())
2446    ```
2447    """
2448
2449    __slots__ = ()
2450
2451    if typing.TYPE_CHECKING:
2452        # We explicitly want this to be `None`.
2453        _net: None  # type: ignore[assignment]
2454
2455    def __init__(self, net: None = None) -> None:
2456        self._net = net

A stand-alone factory that doesn't requires a client instance.

# Example

# We'll implement a serializable RESTClient.
@dataclass(slots=True)
class MyClient(aiobungie.traits.Serializable):
    rest = aiobungie.RESTClient(env["CLIENT_TOKEN"])
    my_name = "Fate怒"
    my_code = 4275

    # Must implement this one method.
    @property
    def factory(self) -> aiobungie.EmptyFactory:
        # Return an empty factory
        return aiobungie.EmptyFactory()

    async def my_memberships(self) -> Sequence[aiobungie.crates.DestinyMembership]:
        # Note, Do not call methods within objects, Since this is an empty
        # factory, The client reference that makes these calls will be `None`.
        response = await self.rest.fetch_membership(self.my_name, self.my_code)
        return self.factory.deserialize_destiny_memberships(response)


    async def main() -> None:
        client = MyClient()
        async with client.client:
            print(await client.my_memberships())

asyncio.run(main())
EmptyFactory(net: None = None)
2455    def __init__(self, net: None = None) -> None:
2456        self._net = net
Inherited Members
Factory
deserialize_bungie_user
deserialize_partial_bungie_user
deserialize_destiny_membership
deserialize_destiny_memberships
deserialize_user
deserialize_searched_user
deserialize_user_credentials
deserialize_user_themes
deserialize_clan
deserialize_clan_member
deserialize_clan_members
deserialize_group_member
deserialize_clan_conversations
deserialize_app_owner
deserialize_app
deserialize_profile
deserialize_profile_item
deserialize_objectives
deserialize_records
deserialize_character_records
deserialize_character_dye
deserialize_character_customization
deserialize_character_minimal_equipments
deserialize_character_render_data
deserialize_available_activity
deserialize_character_activity
deserialize_profile_items
deserialize_progressions
deserialize_milestone
deserialize_characters
deserialize_character
deserialize_character_equipments
deserialize_character_activities
deserialize_characters_render_data
deserialize_character_progressions
deserialize_character_progressions_mapping
deserialize_characters_records
deserialize_profile_records
deserialize_craftables_component
deserialize_components
deserialize_items_component
deserialize_character_component
deserialize_inventory_results
deserialize_inventory_entity
deserialize_objective_entity
deserialize_activity
deserialize_activities
deserialize_extended_weapon_values
deserialize_post_activity_player
deserialize_post_activity
deserialize_aggregated_activity
deserialize_aggregated_activities
deserialize_linked_profiles
deserialize_clan_banners
deserialize_public_milestone_content
deserialize_friend
deserialize_friends
deserialize_friend_requests
deserialize_fireteams
deserialize_fireteam_destiny_users
deserialize_fireteam_members
deserialize_available_fireteam
deserialize_available_fireteams
deserialize_fireteam_party
deserialize_seasonal_artifact
deserialize_profile_progression
deserialize_instanced_item
deserialize_item_energy
deserialize_item_perk
deserialize_item_socket
deserialize_item_stats_view
deserialize_plug_item_state
class Enum(enum.Enum):
69class Enum(__enum.Enum):
70    """Builtin Python enum with extra handlings."""
71
72    @property
73    def name(self) -> str:  # type: ignore[override]
74        return self._name_
75
76    @property
77    def value(self) -> typing.Any:  # type: ignore[override]
78        return self._value_
79
80    def __str__(self) -> str:
81        return self._name_
82
83    def __repr__(self) -> str:
84        return f"<{type(self).__name__}.{self._name_}: {self._value_!s}>"
85
86    def __int__(self) -> int:
87        return int(self.value)

Builtin Python enum with extra handlings.

name: str

The name of the Enum member.

value: Any

The value of the Enum member.

class Factory(aiobungie.interfaces.factory.FactoryInterface):
  60class Factory(interfaces.FactoryInterface):
  61    """The base deserialization factory class for all aiobungie objects.
  62
  63    This entity factory is used to deserialize JSON responses from the REST client and turning them
  64    into a `aiobungie.crates` Python classes.
  65    """
  66
  67    __slots__ = ("_net",)
  68
  69    def __init__(self, net: traits.Netrunner) -> None:
  70        self._net = net
  71
  72    def deserialize_bungie_user(self, data: typedefs.JSONObject) -> user.BungieUser:
  73        return user.BungieUser(
  74            id=int(data["membershipId"]),
  75            created_at=time.clean_date(data["firstAccess"]),
  76            name=data.get("cachedBungieGlobalDisplayName"),
  77            is_deleted=data["isDeleted"],
  78            about=data["about"],
  79            updated_at=time.clean_date(data["lastUpdate"]),
  80            psn_name=data.get("psnDisplayName", None),
  81            stadia_name=data.get("stadiaDisplayName", None),
  82            steam_name=data.get("steamDisplayName", None),
  83            twitch_name=data.get("twitchDisplayName", None),
  84            blizzard_name=data.get("blizzardDisplayName", None),
  85            status=data["statusText"],
  86            locale=data["locale"],
  87            picture=assets.Image(path=str(data["profilePicturePath"])),
  88            code=data.get("cachedBungieGlobalDisplayNameCode", None),
  89            unique_name=data.get("uniqueName", None),
  90            theme_id=int(data["profileTheme"]),
  91            show_activity=bool(data["showActivity"]),
  92            theme_name=data["profileThemeName"],
  93            display_title=data["userTitleDisplay"],
  94        )
  95
  96    def deserialize_partial_bungie_user(
  97        self, payload: typedefs.JSONObject
  98    ) -> user.PartialBungieUser:
  99        return user.PartialBungieUser(
 100            net=self._net,
 101            types=[
 102                enums.MembershipType(type_)
 103                for type_ in payload.get("applicableMembershipTypes", [])
 104            ],
 105            name=payload.get("displayName"),
 106            id=int(payload["membershipId"]),
 107            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
 108            is_public=payload["isPublic"],
 109            icon=assets.Image(payload.get("iconPath", "")),
 110            type=enums.MembershipType(payload["membershipType"]),
 111        )
 112
 113    def deserialize_destiny_membership(
 114        self, payload: typedefs.JSONObject
 115    ) -> user.DestinyMembership:
 116        name: str | None = None
 117        if (raw_name := payload.get("bungieGlobalDisplayName")) is not None:
 118            name = typedefs.unknown(raw_name)
 119
 120        return user.DestinyMembership(
 121            net=self._net,
 122            id=int(payload["membershipId"]),
 123            name=name,
 124            code=payload.get("bungieGlobalDisplayNameCode", None),
 125            last_seen_name=payload.get("LastSeenDisplayName")
 126            or payload.get("displayName")  # noqa: W503
 127            or "",  # noqa: W503
 128            type=enums.MembershipType(payload["membershipType"]),
 129            is_public=payload["isPublic"],
 130            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
 131            icon=assets.Image(payload.get("iconPath", "")),
 132            types=[
 133                enums.MembershipType(type_)
 134                for type_ in payload.get("applicableMembershipTypes", [])
 135            ],
 136        )
 137
 138    def deserialize_destiny_memberships(
 139        self, data: typedefs.JSONArray
 140    ) -> collections.Sequence[user.DestinyMembership]:
 141        return [self.deserialize_destiny_membership(membership) for membership in data]
 142
 143    def deserialize_user(self, data: typedefs.JSONObject) -> user.User:
 144        primary_membership_id: int | None = None
 145        if raw_primary_id := data.get("primaryMembershipId"):
 146            primary_membership_id = int(raw_primary_id)
 147
 148        return user.User(
 149            bungie_user=self.deserialize_bungie_user(data["bungieNetUser"]),
 150            memberships=self.deserialize_destiny_memberships(
 151                data["destinyMemberships"]
 152            ),
 153            primary_membership_id=primary_membership_id,
 154        )
 155
 156    def deserialize_searched_user(
 157        self, payload: typedefs.JSONObject
 158    ) -> user.SearchableDestinyUser:
 159        code: int | None = None
 160        if raw_code := payload.get("bungieGlobalDisplayNameCode"):
 161            code = int(raw_code)
 162
 163        bungie_id: int | None = None
 164        if raw_bungie_id := payload.get("bungieNetMembershipId"):
 165            bungie_id = int(raw_bungie_id)
 166
 167        return user.SearchableDestinyUser(
 168            name=typedefs.unknown(payload["bungieGlobalDisplayName"]),
 169            code=code,
 170            bungie_id=bungie_id,
 171            memberships=self.deserialize_destiny_memberships(
 172                payload["destinyMemberships"]
 173            ),
 174        )
 175
 176    def deserialize_user_credentials(
 177        self, payload: typedefs.JSONArray
 178    ) -> collections.Sequence[user.UserCredentials]:
 179        return [
 180            user.UserCredentials(
 181                type=enums.CredentialType(int(creds["credentialType"])),
 182                display_name=creds["credentialDisplayName"],
 183                is_public=creds["isPublic"],
 184                self_as_string=creds.get("credentialAsString"),
 185            )
 186            for creds in payload
 187        ]
 188
 189    def deserialize_user_themes(
 190        self, payload: typedefs.JSONArray
 191    ) -> collections.Sequence[user.UserThemes]:
 192        return [
 193            user.UserThemes(
 194                id=int(entry["userThemeId"]),
 195                name=entry["userThemeName"] if "userThemeName" in entry else None,
 196                description=entry["userThemeDescription"]
 197                if "userThemeDescription" in entry
 198                else None,
 199            )
 200            for entry in payload
 201        ]
 202
 203    def deserialize_clan(self, payload: typedefs.JSONObject) -> clans.Clan:
 204        # This is kinda redundant
 205        data = payload
 206
 207        # This is always outside the details.
 208        current_user_map: collections.Mapping[str, clans.ClanMember] = {}
 209        if raw_current_user_map := payload.get("currentUserMemberMap"):
 210            current_user_map = {
 211                membership_type: self.deserialize_clan_member(membership)
 212                for membership_type, membership in raw_current_user_map.items()
 213            }
 214
 215        try:
 216            data = payload["detail"]
 217        except KeyError:
 218            pass
 219
 220        id = data["groupId"]
 221        name = data["name"]
 222        created_at = data["creationDate"]
 223        member_count = data["memberCount"]
 224        about = data["about"]
 225        motto = data["motto"]
 226        is_public = data["isPublic"]
 227        banner = assets.Image(str(data["bannerPath"]))
 228        avatar = assets.Image(str(data["avatarPath"]))
 229        tags = data["tags"]
 230        type = data["groupType"]
 231
 232        features = data["features"]
 233        features_obj = clans.ClanFeatures(
 234            max_members=features["maximumMembers"],
 235            max_membership_types=features["maximumMembershipsOfGroupType"],
 236            capabilities=features["capabilities"],
 237            membership_types=features["membershipTypes"],
 238            invite_permissions=features["invitePermissionOverride"],
 239            update_banner_permissions=features["updateBannerPermissionOverride"],
 240            update_culture_permissions=features["updateCulturePermissionOverride"],
 241            join_level=features["joinLevel"],
 242        )
 243
 244        information: typedefs.JSONObject = data["clanInfo"]
 245        progression: collections.Mapping[int, progressions.Progression] = {
 246            int(prog_hash): self.deserialize_progressions(prog)
 247            for prog_hash, prog in information["d2ClanProgressions"].items()
 248        }
 249
 250        return clans.Clan(
 251            net=self._net,
 252            id=int(id),
 253            name=name,
 254            type=enums.GroupType(type),
 255            created_at=time.clean_date(created_at),
 256            member_count=member_count,
 257            motto=motto,
 258            about=about,
 259            is_public=is_public,
 260            banner=banner,
 261            avatar=avatar,
 262            tags=tags,
 263            features=features_obj,
 264            owner=self.deserialize_clan_member(payload["founder"])
 265            if "founder" in payload
 266            else None,
 267            progressions=progression,
 268            call_sign=information["clanCallsign"],
 269            banner_data=information["clanBannerData"],
 270            chat_security=data["chatSecurity"],
 271            conversation_id=int(data["conversationId"]),
 272            allow_chat=data["allowChat"],
 273            theme=data["theme"],
 274            current_user_membership=current_user_map,
 275        )
 276
 277    def deserialize_clan_member(self, data: typedefs.JSONObject, /) -> clans.ClanMember:
 278        destiny_user = self.deserialize_destiny_membership(data["destinyUserInfo"])
 279        return clans.ClanMember(
 280            net=self._net,
 281            last_seen_name=destiny_user.last_seen_name,
 282            id=destiny_user.id,
 283            name=destiny_user.name,
 284            icon=destiny_user.icon,
 285            last_online=time.from_timestamp(int(data["lastOnlineStatusChange"])),
 286            group_id=int(data["groupId"]),
 287            joined_at=time.clean_date(data["joinDate"]),
 288            types=destiny_user.types,
 289            is_public=destiny_user.is_public,
 290            type=destiny_user.type,
 291            code=destiny_user.code,
 292            is_online=data["isOnline"],
 293            crossave_override=destiny_user.crossave_override,
 294            bungie_user=self.deserialize_partial_bungie_user(data["bungieNetUserInfo"])
 295            if "bungieNetUserInfo" in data
 296            else None,
 297            member_type=enums.ClanMemberType(int(data["memberType"])),
 298        )
 299
 300    def deserialize_clan_members(
 301        self, data: typedefs.JSONObject, /
 302    ) -> iterators.Iterator[clans.ClanMember]:
 303        return iterators.Iterator(
 304            [self.deserialize_clan_member(member) for member in data["results"]]
 305        )
 306
 307    def deserialize_group_member(
 308        self, payload: typedefs.JSONObject
 309    ) -> clans.GroupMember:
 310        member = payload["member"]
 311        return clans.GroupMember(
 312            net=self._net,
 313            join_date=time.clean_date(member["joinDate"]),
 314            group_id=int(member["groupId"]),
 315            member_type=enums.ClanMemberType(member["memberType"]),
 316            is_online=member["isOnline"],
 317            last_online=time.from_timestamp(int(member["lastOnlineStatusChange"])),
 318            inactive_memberships=payload.get("areAllMembershipsInactive", None),
 319            member=self.deserialize_destiny_membership(member["destinyUserInfo"]),
 320            group=self.deserialize_clan(payload["group"]),
 321        )
 322
 323    def _deserialize_clan_conversation(
 324        self, payload: typedefs.JSONObject
 325    ) -> clans.ClanConversation:
 326        return clans.ClanConversation(
 327            net=self._net,
 328            id=int(payload["conversationId"]),
 329            group_id=int(payload["groupId"]),
 330            name=typedefs.unknown(payload["chatName"]),
 331            chat_enabled=payload["chatEnabled"],
 332            security=payload["chatSecurity"],
 333        )
 334
 335    def deserialize_clan_conversations(
 336        self, payload: typedefs.JSONArray
 337    ) -> collections.Sequence[clans.ClanConversation]:
 338        return [self._deserialize_clan_conversation(conv) for conv in payload]
 339
 340    def deserialize_app_owner(
 341        self, payload: typedefs.JSONObject
 342    ) -> application.ApplicationOwner:
 343        return application.ApplicationOwner(
 344            net=self._net,
 345            name=payload.get("bungieGlobalDisplayName"),
 346            id=int(payload["membershipId"]),
 347            type=enums.MembershipType(payload["membershipType"]),
 348            icon=assets.Image(str(payload["iconPath"])),
 349            is_public=payload["isPublic"],
 350            code=payload.get("bungieGlobalDisplayNameCode", None),
 351        )
 352
 353    def deserialize_app(self, payload: typedefs.JSONObject) -> application.Application:
 354        return application.Application(
 355            id=int(payload["applicationId"]),
 356            name=payload["name"],
 357            link=payload["link"],
 358            status=payload["status"],
 359            redirect_url=payload.get("redirectUrl", None),
 360            created_at=time.clean_date(str(payload["creationDate"])),
 361            published_at=time.clean_date(str(payload["firstPublished"])),
 362            owner=self.deserialize_app_owner(payload["team"][0]["user"]),  # type: ignore
 363            scope=payload.get("scope"),
 364        )
 365
 366    def _set_character_attrs(self, payload: typedefs.JSONObject) -> character.Character:
 367        return character.Character(
 368            net=self._net,
 369            id=int(payload["characterId"]),
 370            gender=enums.Gender(payload["genderType"]),
 371            race=enums.Race(payload["raceType"]),
 372            class_type=enums.Class(payload["classType"]),
 373            emblem=assets.Image(payload["emblemBackgroundPath"])
 374            if "emblemBackgroundPath" in payload
 375            else None,
 376            emblem_icon=assets.Image(payload["emblemPath"])
 377            if "emblemPath" in payload
 378            else None,
 379            emblem_hash=int(payload["emblemHash"]) if "emblemHash" in payload else None,
 380            last_played=time.clean_date(payload["dateLastPlayed"]),
 381            total_played_time=int(payload["minutesPlayedTotal"]),
 382            member_id=int(payload["membershipId"]),
 383            member_type=enums.MembershipType(payload["membershipType"]),
 384            level=payload["baseCharacterLevel"],
 385            title_hash=payload.get("titleRecordHash", None),
 386            light=payload["light"],
 387            stats={enums.Stat(int(k)): v for k, v in payload["stats"].items()},
 388        )
 389
 390    def deserialize_profile(self, payload: typedefs.JSONObject, /) -> profile.Profile:
 391        payload = payload["data"]
 392        id = int(payload["userInfo"]["membershipId"])
 393        name = payload["userInfo"]["displayName"]
 394        is_public = payload["userInfo"]["isPublic"]
 395        type = enums.MembershipType(payload["userInfo"]["membershipType"])
 396        last_played = time.clean_date(str(payload["dateLastPlayed"]))
 397        character_ids = [int(cid) for cid in payload["characterIds"]]
 398        power_cap = payload["currentSeasonRewardPowerCap"]
 399
 400        return profile.Profile(
 401            id=int(id),
 402            name=name,
 403            is_public=is_public,
 404            type=type,
 405            last_played=last_played,
 406            character_ids=character_ids,
 407            power_cap=power_cap,
 408            net=self._net,
 409        )
 410
 411    def deserialize_profile_item(
 412        self, payload: typedefs.JSONObject
 413    ) -> profile.ProfileItemImpl:
 414        instance_id: int | None = None
 415        if raw_instance_id := payload.get("itemInstanceId"):
 416            instance_id = int(raw_instance_id)
 417
 418        version_number: int | None = None
 419        if raw_version := payload.get("versionNumber"):
 420            version_number = int(raw_version)
 421
 422        transfer_status = enums.TransferStatus(payload["transferStatus"])
 423
 424        return profile.ProfileItemImpl(
 425            net=self._net,
 426            hash=payload["itemHash"],
 427            quantity=payload["quantity"],
 428            bind_status=enums.ItemBindStatus(payload["bindStatus"]),
 429            location=enums.ItemLocation(payload["location"]),
 430            bucket=payload["bucketHash"],
 431            transfer_status=transfer_status,
 432            lockable=payload["lockable"],
 433            state=enums.ItemState(payload["state"]),
 434            dismantle_permissions=payload["dismantlePermission"],
 435            is_wrapper=payload["isWrapper"],
 436            instance_id=instance_id,
 437            version_number=version_number,
 438            ornament_id=payload.get("overrideStyleItemHash"),
 439        )
 440
 441    def deserialize_objectives(self, payload: typedefs.JSONObject) -> records.Objective:
 442        return records.Objective(
 443            net=self._net,
 444            hash=payload["objectiveHash"],
 445            visible=payload["visible"],
 446            complete=payload["complete"],
 447            completion_value=payload["completionValue"],
 448            progress=payload.get("progress"),
 449            destination_hash=payload.get("destinationHash"),
 450            activity_hash=payload.get("activityHash"),
 451        )
 452
 453    # TODO: Remove **nodes and get it directly from the payload.
 454    def deserialize_records(
 455        self,
 456        payload: typedefs.JSONObject,
 457        scores: records.RecordScores | None = None,
 458        **nodes: int,
 459    ) -> records.Record:
 460        objectives: list[records.Objective] | None = None
 461        interval_objectives: list[records.Objective] | None = None
 462        record_state: records.RecordState | int
 463
 464        record_state = records.RecordState(payload["state"])
 465
 466        if raw_objs := payload.get("objectives"):
 467            objectives = [self.deserialize_objectives(obj) for obj in raw_objs]
 468
 469        if raw_interval_objs := payload.get("intervalObjectives"):
 470            interval_objectives = [
 471                self.deserialize_objectives(obj) for obj in raw_interval_objs
 472            ]
 473
 474        return records.Record(
 475            scores=scores,
 476            categories_node_hash=nodes.get("categories_hash"),
 477            seals_node_hash=nodes.get("seals_hash"),
 478            state=record_state,
 479            objectives=objectives,
 480            interval_objectives=interval_objectives,
 481            redeemed_count=payload.get("intervalsRedeemedCount", 0),
 482            completion_times=payload.get("completedCount", None),
 483            reward_visibility=payload.get("rewardVisibility"),
 484        )
 485
 486    def deserialize_character_records(
 487        self,
 488        payload: typedefs.JSONObject,
 489        scores: records.RecordScores | None = None,
 490        record_hashes: list[int] | None = None,
 491    ) -> records.CharacterRecord:
 492        record = self.deserialize_records(payload, scores)
 493        return records.CharacterRecord(
 494            scores=scores,
 495            categories_node_hash=record.categories_node_hash,
 496            seals_node_hash=record.seals_node_hash,
 497            state=record.state,
 498            objectives=record.objectives,
 499            interval_objectives=record.interval_objectives,
 500            redeemed_count=payload.get("intervalsRedeemedCount", 0),
 501            completion_times=payload.get("completedCount"),
 502            reward_visibility=payload.get("rewardVisibility"),
 503            record_hashes=record_hashes or [],
 504        )
 505
 506    def deserialize_character_dye(self, payload: typedefs.JSONObject) -> character.Dye:
 507        return character.Dye(
 508            channel_hash=payload["channelHash"], dye_hash=payload["dyeHash"]
 509        )
 510
 511    def deserialize_character_customization(
 512        self, payload: typedefs.JSONObject
 513    ) -> character.CustomizationOptions:
 514        return character.CustomizationOptions(
 515            personality=payload["personality"],
 516            face=payload["face"],
 517            skin_color=payload["skinColor"],
 518            lip_color=payload["lipColor"],
 519            eye_color=payload["eyeColor"],
 520            hair_colors=payload.get("hairColors", []),
 521            feature_colors=payload.get("featureColors", []),
 522            decal_color=payload["decalColor"],
 523            wear_helmet=payload["wearHelmet"],
 524            hair_index=payload["hairIndex"],
 525            feature_index=payload["featureIndex"],
 526            decal_index=payload["decalIndex"],
 527        )
 528
 529    def deserialize_character_minimal_equipments(
 530        self, payload: typedefs.JSONObject
 531    ) -> character.MinimalEquipments:
 532        if raw_dyes := payload.get("dyes"):
 533            dyes = [self.deserialize_character_dye(dye) for dye in raw_dyes]
 534        else:
 535            dyes = []
 536
 537        return character.MinimalEquipments(
 538            net=self._net, item_hash=payload["itemHash"], dyes=dyes
 539        )
 540
 541    def deserialize_character_render_data(
 542        self, payload: typedefs.JSONObject, /
 543    ) -> character.RenderedData:
 544        return character.RenderedData(
 545            net=self._net,
 546            customization=self.deserialize_character_customization(
 547                payload["customization"]
 548            ),
 549            custom_dyes=[
 550                self.deserialize_character_dye(dye)
 551                for dye in payload["customDyes"]
 552                if dye
 553            ],
 554            equipment=[
 555                self.deserialize_character_minimal_equipments(equipment)
 556                for equipment in payload["peerView"]["equipment"]
 557            ],
 558        )
 559
 560    def deserialize_available_activity(
 561        self, payload: typedefs.JSONObject
 562    ) -> activity.AvailableActivity:
 563        return activity.AvailableActivity(
 564            hash=payload["activityHash"],
 565            is_new=payload["isNew"],
 566            is_completed=payload["isCompleted"],
 567            is_visible=payload["isVisible"],
 568            display_level=payload.get("displayLevel"),
 569            recommended_light=payload.get("recommendedLight"),
 570            difficulty=activity.Difficulty(payload["difficultyTier"]),
 571            can_join=payload["canJoin"],
 572            can_lead=payload["canLead"],
 573        )
 574
 575    def deserialize_character_activity(
 576        self, payload: typedefs.JSONObject
 577    ) -> activity.CharacterActivity:
 578        current_mode: enums.GameMode | None = None
 579        if raw_current_mode := payload.get("currentActivityModeType"):
 580            current_mode = enums.GameMode(raw_current_mode)
 581
 582        if raw_current_modes := payload.get("currentActivityModeTypes"):
 583            current_mode_types = [enums.GameMode(type_) for type_ in raw_current_modes]
 584        else:
 585            current_mode_types = []
 586
 587        return activity.CharacterActivity(
 588            date_started=time.clean_date(payload["dateActivityStarted"]),
 589            current_hash=payload["currentActivityHash"],
 590            current_mode_hash=payload["currentActivityModeHash"],
 591            current_mode=current_mode,
 592            current_mode_hashes=payload.get("currentActivityModeHashes", []),
 593            current_mode_types=current_mode_types,
 594            current_playlist_hash=payload.get("currentPlaylistActivityHash"),
 595            last_story_hash=payload["lastCompletedStoryHash"],
 596            available_activities=[
 597                self.deserialize_available_activity(activity_)
 598                for activity_ in payload["availableActivities"]
 599            ],
 600        )
 601
 602    def deserialize_profile_items(
 603        self, payload: typedefs.JSONObject, /
 604    ) -> list[profile.ProfileItemImpl]:
 605        return [self.deserialize_profile_item(item) for item in payload["items"]]
 606
 607    def _deserialize_node(self, payload: typedefs.JSONObject) -> records.Node:
 608        return records.Node(
 609            state=int(payload["state"]),
 610            objective=self.deserialize_objectives(payload["objective"])
 611            if "objective" in payload
 612            else None,
 613            progress_value=int(payload["progressValue"]),
 614            completion_value=int(payload["completionValue"]),
 615            record_category_score=int(payload["recordCategoryScore"])
 616            if "recordCategoryScore" in payload
 617            else None,
 618        )
 619
 620    @staticmethod
 621    def _deserialize_collectible(payload: typedefs.JSONObject) -> items.Collectible:
 622        recent_collectibles: collections.Collection[int] | None = None
 623        if raw_recent_collectibles := payload.get("recentCollectibleHashes"):
 624            recent_collectibles = [
 625                int(item_hash) for item_hash in raw_recent_collectibles
 626            ]
 627
 628        collectibles: dict[int, int] = {}
 629        for item_hash, mapping in payload["collectibles"].items():
 630            collectibles[int(item_hash)] = int(mapping["state"])
 631
 632        return items.Collectible(
 633            recent_collectibles=recent_collectibles,
 634            collectibles=collectibles,
 635            collection_category_hash=int(payload["collectionCategoriesRootNodeHash"]),
 636            collection_badges_hash=int(payload["collectionBadgesRootNodeHash"]),
 637        )
 638
 639    @staticmethod
 640    def _deserialize_currencies(
 641        payload: typedefs.JSONObject,
 642    ) -> collections.Sequence[items.Currency]:
 643        return [
 644            items.Currency(hash=int(item_hash), amount=int(amount))
 645            for item_hash, amount in payload["itemQuantities"].items()
 646        ]
 647
 648    def deserialize_progressions(
 649        self, payload: typedefs.JSONObject
 650    ) -> progressions.Progression:
 651        return progressions.Progression(
 652            hash=int(payload["progressionHash"]),
 653            level=int(payload["level"]),
 654            cap=int(payload["levelCap"]),
 655            daily_limit=int(payload["dailyLimit"]),
 656            weekly_limit=int(payload["weeklyLimit"]),
 657            current_progress=int(payload["currentProgress"]),
 658            daily_progress=int(payload["dailyProgress"]),
 659            needed=int(payload["progressToNextLevel"]),
 660            next_level=int(payload["nextLevelAt"]),
 661        )
 662
 663    def _deserialize_factions(
 664        self, payload: typedefs.JSONObject
 665    ) -> progressions.Factions:
 666        progs = self.deserialize_progressions(payload)
 667        return progressions.Factions(
 668            hash=progs.hash,
 669            level=progs.level,
 670            cap=progs.cap,
 671            daily_limit=progs.daily_limit,
 672            weekly_limit=progs.weekly_limit,
 673            current_progress=progs.current_progress,
 674            daily_progress=progs.daily_progress,
 675            needed=progs.needed,
 676            next_level=progs.next_level,
 677            faction_hash=payload["factionHash"],
 678            faction_vendor_hash=payload["factionVendorIndex"],
 679        )
 680
 681    def _deserialize_milestone_available_quest(
 682        self, payload: typedefs.JSONObject
 683    ) -> milestones.MilestoneQuest:
 684        return milestones.MilestoneQuest(
 685            item_hash=payload["questItemHash"],
 686            status=self._deserialize_milestone_quest_status(payload["status"]),
 687        )
 688
 689    def _deserialize_milestone_activity(
 690        self, payload: typedefs.JSONObject
 691    ) -> milestones.MilestoneActivity:
 692        phases: collections.Sequence[milestones.MilestoneActivityPhase] | None = None
 693        if raw_phases := payload.get("phases"):
 694            phases = [
 695                milestones.MilestoneActivityPhase(
 696                    is_completed=obj["complete"], hash=obj["phaseHash"]
 697                )
 698                for obj in raw_phases
 699            ]
 700
 701        return milestones.MilestoneActivity(
 702            hash=payload["activityHash"],
 703            challenges=[
 704                self.deserialize_objectives(obj["objective"])
 705                for obj in payload["challenges"]
 706            ],
 707            modifier_hashes=payload.get("modifierHashes"),
 708            boolean_options=payload.get("booleanActivityOptions"),
 709            phases=phases,
 710        )
 711
 712    def _deserialize_milestone_quest_status(
 713        self, payload: typedefs.JSONObject
 714    ) -> milestones.QuestStatus:
 715        return milestones.QuestStatus(
 716            net=self._net,
 717            quest_hash=payload["questHash"],
 718            step_hash=payload["stepHash"],
 719            step_objectives=[
 720                self.deserialize_objectives(objective)
 721                for objective in payload["stepObjectives"]
 722            ],
 723            is_tracked=payload["tracked"],
 724            is_completed=payload["completed"],
 725            started=payload["started"],
 726            item_instance_id=payload["itemInstanceId"],
 727            vendor_hash=payload.get("vendorHash"),
 728            is_redeemed=payload["redeemed"],
 729        )
 730
 731    def _deserialize_milestone_rewards(
 732        self, payload: typedefs.JSONObject
 733    ) -> milestones.MilestoneReward:
 734        return milestones.MilestoneReward(
 735            category_hash=payload["rewardCategoryHash"],
 736            entries=[
 737                milestones.MilestoneRewardEntry(
 738                    entry_hash=entry["rewardEntryHash"],
 739                    is_earned=entry["earned"],
 740                    is_redeemed=entry["redeemed"],
 741                )
 742                for entry in payload["entries"]
 743            ],
 744        )
 745
 746    def deserialize_milestone(
 747        self, payload: typedefs.JSONObject
 748    ) -> milestones.Milestone:
 749        start_date: datetime.datetime | None = None
 750        if raw_start_date := payload.get("startDate"):
 751            start_date = time.clean_date(raw_start_date)
 752
 753        end_date: datetime.datetime | None = None
 754        if raw_end_date := payload.get("endDate"):
 755            end_date = time.clean_date(raw_end_date)
 756
 757        rewards: collections.Collection[milestones.MilestoneReward] | None = None
 758        if raw_rewards := payload.get("rewards"):
 759            rewards = [
 760                self._deserialize_milestone_rewards(reward) for reward in raw_rewards
 761            ]
 762
 763        activities: collections.Sequence[milestones.MilestoneActivity] | None = None
 764        if raw_activities := payload.get("activities"):
 765            activities = [
 766                self._deserialize_milestone_activity(active)
 767                for active in raw_activities
 768            ]
 769
 770        quests: collections.Sequence[milestones.MilestoneQuest] | None = None
 771        if raw_quests := payload.get("availableQuests"):
 772            quests = [
 773                self._deserialize_milestone_available_quest(quest)
 774                for quest in raw_quests
 775            ]
 776
 777        vendors: collections.Sequence[milestones.MilestoneVendor] | None = None
 778        if raw_vendors := payload.get("vendors"):
 779            vendors = [
 780                milestones.MilestoneVendor(
 781                    vendor_hash=vendor["vendorHash"],
 782                    preview_itemhash=vendor.get("previewItemHash"),
 783                )
 784                for vendor in raw_vendors
 785            ]
 786
 787        return milestones.Milestone(
 788            hash=payload["milestoneHash"],
 789            start_date=start_date,
 790            end_date=end_date,
 791            order=payload["order"],
 792            rewards=rewards,
 793            available_quests=quests,
 794            activities=activities,
 795            vendors=vendors,
 796        )
 797
 798    def _deserialize_artifact_tiers(
 799        self, payload: typedefs.JSONObject
 800    ) -> season.ArtifactTier:
 801        return season.ArtifactTier(
 802            hash=payload["tierHash"],
 803            is_unlocked=payload["isUnlocked"],
 804            points_to_unlock=payload["pointsToUnlock"],
 805            items=[
 806                season.ArtifactTierItem(
 807                    hash=item["itemHash"], is_active=item["isActive"]
 808                )
 809                for item in payload["items"]
 810            ],
 811        )
 812
 813    def deserialize_characters(
 814        self, payload: typedefs.JSONObject
 815    ) -> collections.Mapping[int, character.Character]:
 816        return {
 817            int(char_id): self._set_character_attrs(char)
 818            for char_id, char in payload["data"].items()
 819        }
 820
 821    def deserialize_character(
 822        self, payload: typedefs.JSONObject
 823    ) -> character.Character:
 824        return self._set_character_attrs(payload)
 825
 826    def deserialize_character_equipments(
 827        self, payload: typedefs.JSONObject
 828    ) -> collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]:
 829        return {
 830            int(char_id): self.deserialize_profile_items(item)
 831            for char_id, item in payload["data"].items()
 832        }
 833
 834    def deserialize_character_activities(
 835        self, payload: typedefs.JSONObject
 836    ) -> collections.Mapping[int, activity.CharacterActivity]:
 837        return {
 838            int(char_id): self.deserialize_character_activity(data)
 839            for char_id, data in payload["data"].items()
 840        }
 841
 842    def deserialize_characters_render_data(
 843        self, payload: typedefs.JSONObject
 844    ) -> collections.Mapping[int, character.RenderedData]:
 845        return {
 846            int(char_id): self.deserialize_character_render_data(data)
 847            for char_id, data in payload["data"].items()
 848        }
 849
 850    def deserialize_character_progressions(
 851        self, payload: typedefs.JSONObject
 852    ) -> character.CharacterProgression:
 853        progressions_ = {
 854            int(prog_id): self.deserialize_progressions(prog)
 855            for prog_id, prog in payload["progressions"].items()
 856        }
 857
 858        factions = {
 859            int(faction_id): self._deserialize_factions(faction)
 860            for faction_id, faction in payload["factions"].items()
 861        }
 862
 863        milestones_ = {
 864            int(milestone_hash): self.deserialize_milestone(milestone)
 865            for milestone_hash, milestone in payload["milestones"].items()
 866        }
 867
 868        uninstanced_item_objectives = {
 869            int(item_hash): [self.deserialize_objectives(ins) for ins in obj]
 870            for item_hash, obj in payload["uninstancedItemObjectives"].items()
 871        }
 872
 873        artifact = payload["seasonalArtifact"]
 874        seasonal_artifact = season.CharacterScopedArtifact(
 875            hash=artifact["artifactHash"],
 876            points_used=artifact["pointsUsed"],
 877            reset_count=artifact["resetCount"],
 878            tiers=[
 879                self._deserialize_artifact_tiers(tier) for tier in artifact["tiers"]
 880            ],
 881        )
 882        checklists = payload["checklists"]
 883
 884        return character.CharacterProgression(
 885            progressions=progressions_,
 886            factions=factions,
 887            checklists=checklists,
 888            milestones=milestones_,
 889            seasonal_artifact=seasonal_artifact,
 890            uninstanced_item_objectives=uninstanced_item_objectives,
 891        )
 892
 893    def deserialize_character_progressions_mapping(
 894        self, payload: typedefs.JSONObject
 895    ) -> collections.Mapping[int, character.CharacterProgression]:
 896        character_progressions: collections.Mapping[
 897            int, character.CharacterProgression
 898        ] = {}
 899        for char_id, data in payload["data"].items():
 900            # A little hack to stop mypy complaining about Mapping <-> dict
 901            character_progressions[
 902                int(char_id)
 903            ] = self.deserialize_character_progressions(data)  # type: ignore[index]
 904        return character_progressions
 905
 906    def deserialize_characters_records(
 907        self,
 908        payload: typedefs.JSONObject,
 909    ) -> collections.Mapping[int, records.CharacterRecord]:
 910        return {
 911            int(rec_id): self.deserialize_character_records(
 912                rec, record_hashes=payload.get("featuredRecordHashes")
 913            )
 914            for rec_id, rec in payload["records"].items()
 915        }
 916
 917    def deserialize_profile_records(
 918        self, payload: typedefs.JSONObject
 919    ) -> collections.Mapping[int, records.Record]:
 920        raw_profile_records = payload["data"]
 921        scores = records.RecordScores(
 922            current_score=raw_profile_records["score"],
 923            legacy_score=raw_profile_records["legacyScore"],
 924            lifetime_score=raw_profile_records["lifetimeScore"],
 925        )
 926        return {
 927            int(record_id): self.deserialize_records(
 928                record,
 929                scores,
 930                categories_hash=raw_profile_records["recordCategoriesRootNodeHash"],
 931                seals_hash=raw_profile_records["recordSealsRootNodeHash"],
 932            )
 933            for record_id, record in raw_profile_records["records"].items()
 934        }
 935
 936    def _deserialize_craftable_socket_plug(
 937        self, payload: typedefs.JSONObject
 938    ) -> items.CraftableSocketPlug:
 939        return items.CraftableSocketPlug(
 940            item_hash=int(payload["plugItemHash"]),
 941            failed_requirement_indexes=payload.get("failedRequirementIndexes", []),
 942        )
 943
 944    def _deserialize_craftable_socket(
 945        self, payload: typedefs.JSONObject
 946    ) -> items.CraftableSocket:
 947        plugs: list[items.CraftableSocketPlug] = []
 948        if raw_plug := payload.get("plug"):
 949            plugs.extend(
 950                self._deserialize_craftable_socket_plug(plug) for plug in raw_plug
 951            )
 952
 953        return items.CraftableSocket(
 954            plug_set_hash=int(payload["plugSetHash"]), plugs=plugs
 955        )
 956
 957    def _deserialize_craftable_item(
 958        self, payload: typedefs.JSONObject
 959    ) -> items.CraftableItem:
 960        return items.CraftableItem(
 961            is_visible=payload["visible"],
 962            failed_requirement_indexes=payload.get("failedRequirementIndexes", []),
 963            sockets=[
 964                self._deserialize_craftable_socket(socket)
 965                for socket in payload["sockets"]
 966            ],
 967        )
 968
 969    def deserialize_craftables_component(
 970        self, payload: typedefs.JSONObject
 971    ) -> components.CraftablesComponent:
 972        return components.CraftablesComponent(
 973            net=self._net,
 974            craftables={
 975                int(item_id): self._deserialize_craftable_item(item)
 976                for item_id, item in payload["craftables"].items()
 977                if item is not None
 978            },
 979            crafting_root_node_hash=payload["craftingRootNodeHash"],
 980        )
 981
 982    def deserialize_components(  # noqa: C901 Too complex.
 983        self, payload: typedefs.JSONObject
 984    ) -> components.Component:
 985        # Due to how complex this method is, We'll stick to
 986        # typing.Optional here.
 987
 988        profile_: profile.Profile | None = None
 989        if raw_profile := payload.get("profile"):
 990            profile_ = self.deserialize_profile(raw_profile)
 991
 992        profile_progression: profile.ProfileProgression | None = None
 993        if raw_profile_progression := payload.get("profileProgression"):
 994            profile_progression = self.deserialize_profile_progression(
 995                raw_profile_progression
 996            )
 997
 998        profile_currencies: typing.Optional[
 999            collections.Sequence[profile.ProfileItemImpl]
1000        ] = None
1001        if raw_profile_currencies := payload.get("profileCurrencies"):
1002            if "data" in raw_profile_currencies:
1003                profile_currencies = self.deserialize_profile_items(
1004                    raw_profile_currencies["data"]
1005                )
1006
1007        profile_inventories: typing.Optional[
1008            collections.Sequence[profile.ProfileItemImpl]
1009        ] = None
1010        if raw_profile_inventories := payload.get("profileInventory"):
1011            if "data" in raw_profile_inventories:
1012                profile_inventories = self.deserialize_profile_items(
1013                    raw_profile_inventories["data"]
1014                )
1015
1016        profile_records: typing.Optional[
1017            collections.Mapping[int, records.Record]
1018        ] = None
1019
1020        if raw_profile_records_ := payload.get("profileRecords"):
1021            profile_records = self.deserialize_profile_records(raw_profile_records_)
1022
1023        characters: typing.Optional[
1024            collections.Mapping[int, character.Character]
1025        ] = None
1026        if raw_characters := payload.get("characters"):
1027            characters = self.deserialize_characters(raw_characters)
1028
1029        character_records: typing.Optional[
1030            collections.Mapping[int, records.CharacterRecord]
1031        ] = None
1032
1033        if raw_character_records := payload.get("characterRecords"):
1034            # Had to do it in two steps..
1035            to_update = {}
1036            for _, data in raw_character_records["data"].items():
1037                for record_id, record in data.items():
1038                    to_update[record_id] = record
1039
1040            character_records = {
1041                int(rec_id): self.deserialize_character_records(
1042                    rec, record_hashes=to_update.get("featuredRecordHashes")
1043                )
1044                for rec_id, rec in to_update["records"].items()
1045            }
1046
1047        character_equipments: typing.Optional[
1048            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1049        ] = None
1050        if raw_character_equips := payload.get("characterEquipment"):
1051            character_equipments = self.deserialize_character_equipments(
1052                raw_character_equips
1053            )
1054
1055        character_inventories: typing.Optional[
1056            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1057        ] = None
1058        if raw_character_inventories := payload.get("characterInventories"):
1059            if "data" in raw_character_inventories:
1060                character_inventories = self.deserialize_character_equipments(
1061                    raw_character_inventories
1062                )
1063
1064        character_activities: typing.Optional[
1065            collections.Mapping[int, activity.CharacterActivity]
1066        ] = None
1067        if raw_char_acts := payload.get("characterActivities"):
1068            character_activities = self.deserialize_character_activities(raw_char_acts)
1069
1070        character_render_data: typing.Optional[
1071            collections.Mapping[int, character.RenderedData]
1072        ] = None
1073        if raw_character_render_data := payload.get("characterRenderData"):
1074            character_render_data = self.deserialize_characters_render_data(
1075                raw_character_render_data
1076            )
1077
1078        character_progressions: typing.Optional[
1079            collections.Mapping[int, character.CharacterProgression]
1080        ] = None
1081
1082        if raw_character_progressions := payload.get("characterProgressions"):
1083            character_progressions = self.deserialize_character_progressions_mapping(
1084                raw_character_progressions
1085            )
1086
1087        profile_string_vars: typing.Optional[collections.Mapping[int, int]] = None
1088        if raw_profile_string_vars := payload.get("profileStringVariables"):
1089            profile_string_vars = raw_profile_string_vars["data"]["integerValuesByHash"]
1090
1091        character_string_vars: typing.Optional[
1092            collections.Mapping[int, collections.Mapping[int, int]]
1093        ] = None
1094        if raw_character_string_vars := payload.get("characterStringVariables"):
1095            character_string_vars = {
1096                int(char_id): data["integerValuesByHash"]
1097                for char_id, data in raw_character_string_vars["data"].items()
1098            }
1099
1100        metrics: typing.Optional[
1101            collections.Sequence[
1102                collections.Mapping[int, tuple[bool, records.Objective | None]]
1103            ]
1104        ] = None
1105        root_node_hash: int | None = None
1106
1107        if raw_metrics := payload.get("metrics"):
1108            root_node_hash = raw_metrics["data"]["metricsRootNodeHash"]
1109            metrics = [
1110                {
1111                    int(metrics_hash): (
1112                        data["invisible"],
1113                        self.deserialize_objectives(data["objectiveProgress"])
1114                        if "objectiveProgress" in data
1115                        else None,
1116                    )
1117                    for metrics_hash, data in raw_metrics["data"]["metrics"].items()
1118                }
1119            ]
1120        transitory: fireteams.FireteamParty | None = None
1121        if raw_transitory := payload.get("profileTransitoryData"):
1122            if "data" in raw_transitory:
1123                transitory = self.deserialize_fireteam_party(raw_transitory["data"])
1124
1125        item_components: components.ItemsComponent | None = None
1126        if raw_item_components := payload.get("itemComponents"):
1127            item_components = self.deserialize_items_component(raw_item_components)
1128
1129        profile_plugsets: typing.Optional[
1130            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1131        ] = None
1132
1133        if raw_profile_plugs := payload.get("profilePlugSets"):
1134            profile_plugsets = {
1135                int(index): [self.deserialize_plug_item_state(state) for state in data]
1136                for index, data in raw_profile_plugs["data"]["plugs"].items()
1137            }
1138
1139        character_plugsets: typing.Optional[
1140            collections.Mapping[
1141                int, collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1142            ]
1143        ] = None
1144        if raw_char_plugsets := payload.get("characterPlugSets"):
1145            character_plugsets = {
1146                int(char_id): {
1147                    int(index): [
1148                        self.deserialize_plug_item_state(state) for state in data
1149                    ]
1150                    for index, data in inner["plugs"].items()
1151                }
1152                for char_id, inner in raw_char_plugsets["data"].items()
1153            }
1154
1155        character_collectibles: typing.Optional[
1156            collections.Mapping[int, items.Collectible]
1157        ] = None
1158        if raw_character_collectibles := payload.get("characterCollectibles"):
1159            character_collectibles = {
1160                int(char_id): self._deserialize_collectible(data)
1161                for char_id, data in raw_character_collectibles["data"].items()
1162            }
1163
1164        profile_collectibles: items.Collectible | None = None
1165        if raw_profile_collectibles := payload.get("profileCollectibles"):
1166            profile_collectibles = self._deserialize_collectible(
1167                raw_profile_collectibles["data"]
1168            )
1169
1170        profile_nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1171        if raw_profile_nodes := payload.get("profilePresentationNodes"):
1172            profile_nodes = {
1173                int(node_hash): self._deserialize_node(node)
1174                for node_hash, node in raw_profile_nodes["data"]["nodes"].items()
1175            }
1176
1177        character_nodes: typing.Optional[
1178            collections.Mapping[int, collections.Mapping[int, records.Node]]
1179        ] = None
1180        if raw_character_nodes := payload.get("characterPresentationNodes"):
1181            character_nodes = {
1182                int(char_id): {
1183                    int(node_hash): self._deserialize_node(node)
1184                    for node_hash, node in each_character["nodes"].items()
1185                }
1186                for char_id, each_character in raw_character_nodes["data"].items()
1187            }
1188
1189        platform_silver: typing.Optional[
1190            collections.Mapping[str, profile.ProfileItemImpl]
1191        ] = None
1192        if raw_platform_silver := payload.get("platformSilver"):
1193            if "data" in raw_platform_silver:
1194                platform_silver = {
1195                    platform_name: self.deserialize_profile_item(item)
1196                    for platform_name, item in raw_platform_silver["data"][
1197                        "platformSilver"
1198                    ].items()
1199                }
1200
1201        character_currency_lookups: typing.Optional[
1202            collections.Mapping[int, collections.Sequence[items.Currency]]
1203        ] = None
1204        if raw_char_lookups := payload.get("characterCurrencyLookups"):
1205            if "data" in raw_char_lookups:
1206                character_currency_lookups = {
1207                    int(char_id): self._deserialize_currencies(currency)
1208                    for char_id, currency in raw_char_lookups["data"].items()
1209                }
1210
1211        character_craftables: typing.Optional[
1212            collections.Mapping[int, components.CraftablesComponent]
1213        ] = None
1214        if raw_character_craftables := payload.get("characterCraftables"):
1215            if "data" in raw_character_craftables:
1216                character_craftables = {
1217                    int(char_id): self.deserialize_craftables_component(craftable)
1218                    for char_id, craftable in raw_character_craftables["data"].items()
1219                }
1220
1221        return components.Component(
1222            profiles=profile_,
1223            profile_progression=profile_progression,
1224            profile_currencies=profile_currencies,
1225            profile_inventories=profile_inventories,
1226            profile_records=profile_records,
1227            characters=characters,
1228            character_records=character_records,
1229            character_equipments=character_equipments,
1230            character_inventories=character_inventories,
1231            character_activities=character_activities,
1232            character_render_data=character_render_data,
1233            character_progressions=character_progressions,
1234            profile_string_variables=profile_string_vars,
1235            character_string_variables=character_string_vars,
1236            metrics=metrics,
1237            root_node_hash=root_node_hash,
1238            transitory=transitory,
1239            item_components=item_components,
1240            profile_plugsets=profile_plugsets,
1241            character_plugsets=character_plugsets,
1242            character_collectibles=character_collectibles,
1243            profile_collectibles=profile_collectibles,
1244            profile_nodes=profile_nodes,
1245            character_nodes=character_nodes,
1246            platform_silver=platform_silver,
1247            character_currency_lookups=character_currency_lookups,
1248            character_craftables=character_craftables,
1249        )
1250
1251    def deserialize_items_component(
1252        self, payload: typedefs.JSONObject
1253    ) -> components.ItemsComponent:
1254        # Due to how complex this method is, We'll stick to typing.Optional.
1255        instances: typing.Optional[
1256            collections.Sequence[collections.Mapping[int, items.ItemInstance]]
1257        ] = None
1258        if raw_instances := payload.get("instances"):
1259            instances = [
1260                {
1261                    int(ins_id): self.deserialize_instanced_item(item)
1262                    for ins_id, item in raw_instances["data"].items()
1263                }
1264            ]
1265
1266        render_data: typing.Optional[
1267            collections.Mapping[int, tuple[bool, dict[int, int]]]
1268        ] = None
1269        if raw_render_data := payload.get("renderData"):
1270            render_data = {
1271                int(ins_id): (data["useCustomDyes"], data["artRegions"])
1272                for ins_id, data in raw_render_data["data"].items()
1273            }
1274
1275        stats: typing.Optional[collections.Mapping[int, items.ItemStatsView]] = None
1276        if raw_stats := payload.get("stats"):
1277            builder: collections.Mapping[int, items.ItemStatsView] = {}
1278            for ins_id, stat in raw_stats["data"].items():
1279                for _, items_ in stat.items():
1280                    builder[int(ins_id)] = self.deserialize_item_stats_view(items_)  # type: ignore[index]
1281            stats = builder
1282
1283        sockets: typing.Optional[
1284            collections.Mapping[int, collections.Sequence[items.ItemSocket]]
1285        ] = None
1286        if raw_sockets := payload.get("sockets"):
1287            sockets = {
1288                int(ins_id): [
1289                    self.deserialize_item_socket(socket) for socket in item["sockets"]
1290                ]
1291                for ins_id, item in raw_sockets["data"].items()
1292            }
1293
1294        objectives: typing.Optional[
1295            collections.Mapping[int, collections.Sequence[records.Objective]]
1296        ] = None
1297        if raw_objectives := payload.get("objectives"):
1298            objectives = {
1299                int(ins_id): [self.deserialize_objectives(objective)]
1300                for ins_id, data in raw_objectives["data"].items()
1301                for objective in data["objectives"]
1302            }
1303
1304        perks: typing.Optional[
1305            collections.Mapping[int, collections.Collection[items.ItemPerk]]
1306        ] = None
1307        if raw_perks := payload.get("perks"):
1308            perks = {
1309                int(ins_id): [
1310                    self.deserialize_item_perk(perk) for perk in item["perks"]
1311                ]
1312                for ins_id, item in raw_perks["data"].items()
1313            }
1314
1315        plug_states: typing.Optional[collections.Sequence[items.PlugItemState]] = None
1316        if raw_plug_states := payload.get("plugStates"):
1317            pending_states: list[items.PlugItemState] = []
1318            for _, plug in raw_plug_states["data"].items():
1319                pending_states.append(self.deserialize_plug_item_state(plug))
1320            plug_states = pending_states
1321
1322        reusable_plugs: typing.Optional[
1323            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1324        ] = None
1325        if raw_re_plugs := payload.get("reusablePlugs"):
1326            reusable_plugs = {
1327                int(ins_id): [
1328                    self.deserialize_plug_item_state(state) for state in inner
1329                ]
1330                for ins_id, plug in raw_re_plugs["data"].items()
1331                for inner in list(plug["plugs"].values())
1332            }
1333
1334        plug_objectives: typing.Optional[
1335            collections.Mapping[
1336                int, collections.Mapping[int, collections.Collection[records.Objective]]
1337            ]
1338        ] = None
1339        if raw_plug_objectives := payload.get("plugObjectives"):
1340            plug_objectives = {
1341                int(ins_id): {
1342                    int(obj_hash): [self.deserialize_objectives(obj) for obj in objs]
1343                    for obj_hash, objs in inner["objectivesPerPlug"].items()
1344                }
1345                for ins_id, inner in raw_plug_objectives["data"].items()
1346            }
1347
1348        return components.ItemsComponent(
1349            sockets=sockets,
1350            stats=stats,
1351            render_data=render_data,
1352            instances=instances,
1353            objectives=objectives,
1354            perks=perks,
1355            plug_states=plug_states,
1356            reusable_plugs=reusable_plugs,
1357            plug_objectives=plug_objectives,
1358        )
1359
1360    def deserialize_character_component(  # type: ignore[call-arg]
1361        self, payload: typedefs.JSONObject
1362    ) -> components.CharacterComponent:
1363        character_: character.Character | None = None
1364        if raw_singular_character := payload.get("character"):
1365            character_ = self.deserialize_character(raw_singular_character["data"])
1366
1367        inventory: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1368        if raw_inventory := payload.get("inventory"):
1369            if "data" in raw_inventory:
1370                inventory = self.deserialize_profile_items(raw_inventory["data"])
1371
1372        activities: activity.CharacterActivity | None = None
1373        if raw_activities := payload.get("activities"):
1374            activities = self.deserialize_character_activity(raw_activities["data"])
1375
1376        equipment: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1377        if raw_equipments := payload.get("equipment"):
1378            equipment = self.deserialize_profile_items(raw_equipments["data"])
1379
1380        progressions_: character.CharacterProgression | None = None
1381        if raw_progressions := payload.get("progressions"):
1382            progressions_ = self.deserialize_character_progressions(
1383                raw_progressions["data"]
1384            )
1385
1386        render_data: character.RenderedData | None = None
1387        if raw_render_data := payload.get("renderData"):
1388            render_data = self.deserialize_character_render_data(
1389                raw_render_data["data"]
1390            )
1391
1392        character_records: typing.Optional[
1393            collections.Mapping[int, records.CharacterRecord]
1394        ] = None
1395        if raw_char_records := payload.get("records"):
1396            character_records = self.deserialize_characters_records(
1397                raw_char_records["data"]
1398            )
1399
1400        item_components: components.ItemsComponent | None = None
1401        if raw_item_components := payload.get("itemComponents"):
1402            item_components = self.deserialize_items_component(raw_item_components)
1403
1404        nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1405        if raw_nodes := payload.get("presentationNodes"):
1406            nodes = {
1407                int(node_hash): self._deserialize_node(node)
1408                for node_hash, node in raw_nodes["data"]["nodes"].items()
1409            }
1410
1411        collectibles: items.Collectible | None = None
1412        if raw_collectibles := payload.get("collectibles"):
1413            collectibles = self._deserialize_collectible(raw_collectibles["data"])
1414
1415        currency_lookups: typing.Optional[collections.Sequence[items.Currency]] = None
1416        if raw_currencies := payload.get("currencyLookups"):
1417            if "data" in raw_currencies:
1418                currency_lookups = self._deserialize_currencies(raw_currencies)
1419
1420        return components.CharacterComponent(
1421            activities=activities,
1422            equipment=equipment,
1423            inventory=inventory,
1424            progressions=progressions_,
1425            render_data=render_data,
1426            character=character_,
1427            character_records=character_records,
1428            profile_records=None,
1429            item_components=item_components,
1430            currency_lookups=currency_lookups,
1431            collectibles=collectibles,
1432            nodes=nodes,
1433        )
1434
1435    def _set_entity_attrs(
1436        self, payload: typedefs.JSONObject, *, key: str = "displayProperties"
1437    ) -> entity.Entity:
1438        properties = payload[key]
1439        name = typedefs.unknown(properties["name"])
1440        description = typedefs.unknown(properties["description"])
1441
1442        return entity.Entity(
1443            net=self._net,
1444            hash=payload["hash"],
1445            index=payload["index"],
1446            name=name,
1447            description=description,
1448            has_icon=properties["hasIcon"],
1449            icon=assets.Image.default_or_else(properties.get("icon")),
1450        )
1451
1452    def deserialize_inventory_results(
1453        self, payload: typedefs.JSONObject
1454    ) -> iterators.Iterator[entity.SearchableEntity]:
1455        return iterators.Iterator(
1456            [
1457                entity.SearchableEntity(
1458                    net=self._net,
1459                    hash=data["hash"],
1460                    entity_type=data["entityType"],
1461                    weight=data["weight"],
1462                    suggested_words=payload["suggestedWords"],
1463                    name=data["displayProperties"]["name"],
1464                    has_icon=data["displayProperties"]["hasIcon"],
1465                    description=typedefs.unknown(
1466                        data["displayProperties"]["description"]
1467                    ),
1468                    icon=assets.Image(data["displayProperties"]["icon"]),
1469                )
1470                for data in payload["results"]["results"]
1471            ]
1472        )
1473
1474    def _deserialize_inventory_item_objects(
1475        self, payload: typedefs.JSONObject
1476    ) -> entity.InventoryEntityObjects:
1477        return entity.InventoryEntityObjects(
1478            action=payload.get("action"),
1479            set_data=payload.get("setData"),
1480            stats=payload.get("stats"),
1481            equipping_block=payload.get("equippingBlock"),
1482            translation_block=payload.get("translationBlock"),
1483            preview=payload.get("preview"),
1484            quality=payload.get("quality"),
1485            value=payload.get("value"),
1486            source_data=payload.get("sourceData"),
1487            objectives=payload.get("objectives"),
1488            plug=payload.get("plug"),
1489            metrics=payload.get("metrics"),
1490            gearset=payload.get("gearset"),
1491            sack=payload.get("sack"),
1492            sockets=payload.get("sockets"),
1493            summary=payload.get("summary"),
1494            talent_gird=payload.get("talentGrid"),
1495            investments_stats=payload.get("investmentStats"),
1496            perks=payload.get("perks"),
1497            animations=payload.get("animations", []),
1498            links=payload.get("links", []),
1499        )
1500
1501    def deserialize_inventory_entity(  # noqa: C901 Too complex.
1502        self, payload: typedefs.JSONObject, /
1503    ) -> entity.InventoryEntity:
1504        props = self._set_entity_attrs(payload)
1505        objects = self._deserialize_inventory_item_objects(payload)
1506
1507        collectible_hash: int | None = None
1508        if raw_collectible_hash := payload.get("collectibleHash"):
1509            collectible_hash = int(raw_collectible_hash)
1510
1511        secondary_icon: assets.Image | None = None
1512        if raw_second_icon := payload.get("secondaryIcon"):
1513            secondary_icon = assets.Image(raw_second_icon)
1514
1515        secondary_overlay: assets.Image | None = None
1516        if raw_second_overlay := payload.get("secondaryOverlay"):
1517            secondary_overlay = assets.Image(raw_second_overlay)
1518
1519        secondary_special: assets.Image | None = None
1520        if raw_second_special := payload.get("secondarySpecial"):
1521            secondary_special = assets.Image(raw_second_special)
1522
1523        screenshot: assets.Image | None = None
1524        if raw_screenshot := payload.get("screenshot"):
1525            screenshot = assets.Image(raw_screenshot)
1526
1527        watermark_icon: assets.Image | None = None
1528        if raw_watermark_icon := payload.get("iconWatermark"):
1529            watermark_icon = assets.Image(raw_watermark_icon)
1530
1531        watermark_shelved: assets.Image | None = None
1532        if raw_watermark_shelved := payload.get("iconWatermarkShelved"):
1533            watermark_shelved = assets.Image(raw_watermark_shelved)
1534
1535        about: str | None = None
1536        if raw_about := payload.get("flavorText"):
1537            about = raw_about
1538
1539        ui_item_style: str | None = None
1540        if raw_ui_style := payload.get("uiItemDisplayStyle"):
1541            ui_item_style = raw_ui_style
1542
1543        tier_and_name: str | None = None
1544        if raw_tier_and_name := payload.get("itemTypeAndTierDisplayName"):
1545            tier_and_name = raw_tier_and_name
1546
1547        type_name: str | None = None
1548        if raw_type_name := payload.get("itemTypeDisplayName"):
1549            type_name = raw_type_name
1550
1551        display_source: str | None = None
1552        if raw_display_source := payload.get("displaySource"):
1553            display_source = raw_display_source
1554
1555        lorehash: int | None = None
1556        if raw_lore_hash := payload.get("loreHash"):
1557            lorehash = int(raw_lore_hash)
1558
1559        summary_hash: int | None = None
1560        if raw_summary_hash := payload.get("summaryItemHash"):
1561            summary_hash = raw_summary_hash
1562
1563        breaker_type_hash: int | None = None
1564        if raw_breaker_type_hash := payload.get("breakerTypeHash"):
1565            breaker_type_hash = int(raw_breaker_type_hash)
1566
1567        damage_types: typing.Optional[collections.Sequence[int]] = None
1568        if raw_damage_types := payload.get("damageTypes"):
1569            damage_types = [int(type_) for type_ in raw_damage_types]
1570
1571        damagetype_hashes: typing.Optional[collections.Sequence[int]] = None
1572        if raw_damagetype_hashes := payload.get("damageTypeHashes"):
1573            damagetype_hashes = [int(type_) for type_ in raw_damagetype_hashes]
1574
1575        default_damagetype_hash: int | None = None
1576        if raw_defaultdmg_hash := payload.get("defaultDamageTypeHash"):
1577            default_damagetype_hash = int(raw_defaultdmg_hash)
1578
1579        emblem_objective_hash: int | None = None
1580        if raw_emblem_obj_hash := payload.get("emblemObjectiveHash"):
1581            emblem_objective_hash = int(raw_emblem_obj_hash)
1582
1583        tier_type: enums.TierType | None = None
1584        tier: enums.ItemTier | None = None
1585        bucket_hash: int | None = None
1586        recovery_hash: int | None = None
1587        tier_name: str | None = None
1588        isinstance_item: bool = False
1589        expire_tool_tip: str | None = None
1590        expire_in_orbit_message: str | None = None
1591        suppress_expiration: bool = False
1592        max_stack_size: int | None = None
1593        stack_label: str | None = None
1594
1595        if inventory := payload.get("inventory"):
1596            tier_type = enums.TierType(int(inventory["tierType"]))
1597            tier = enums.ItemTier(int(inventory["tierTypeHash"]))
1598            bucket_hash = int(inventory["bucketTypeHash"])
1599            recovery_hash = int(inventory["recoveryBucketTypeHash"])
1600            tier_name = inventory["tierTypeName"]
1601            isinstance_item = inventory["isInstanceItem"]
1602            suppress_expiration = inventory["suppressExpirationWhenObjectivesComplete"]
1603            max_stack_size = int(inventory["maxStackSize"])
1604
1605            try:
1606                stack_label = inventory["stackUniqueLabel"]
1607            except KeyError:
1608                pass
1609
1610        return entity.InventoryEntity(
1611            net=self._net,
1612            collectible_hash=collectible_hash,
1613            name=props.name,
1614            about=about,
1615            emblem_objective_hash=emblem_objective_hash,
1616            suppress_expiration=suppress_expiration,
1617            max_stack_size=max_stack_size,
1618            stack_label=stack_label,
1619            tier=tier,
1620            tier_type=tier_type,
1621            tier_name=tier_name,
1622            bucket_hash=bucket_hash,
1623            recovery_bucket_hash=recovery_hash,
1624            isinstance_item=isinstance_item,
1625            expire_in_orbit_message=expire_in_orbit_message,
1626            expiration_tooltip=expire_tool_tip,
1627            lore_hash=lorehash,
1628            type_and_tier_name=tier_and_name,
1629            summary_hash=summary_hash,
1630            ui_display_style=ui_item_style,
1631            type_name=type_name,
1632            breaker_type_hash=breaker_type_hash,
1633            description=props.description,
1634            display_source=display_source,
1635            hash=props.hash,
1636            damage_types=damage_types,
1637            index=props.index,
1638            icon=props.icon,
1639            has_icon=props.has_icon,
1640            screenshot=screenshot,
1641            watermark_icon=watermark_icon,
1642            watermark_shelved=watermark_shelved,
1643            secondary_icon=secondary_icon,
1644            secondary_overlay=secondary_overlay,
1645            secondary_special=secondary_special,
1646            type=enums.ItemType(int(payload["itemType"])),
1647            trait_hashes=[int(id_) for id_ in payload.get("traitHashes", [])],
1648            trait_ids=[trait for trait in payload.get("traitIds", [])],
1649            category_hashes=[int(hash_) for hash_ in payload["itemCategoryHashes"]],
1650            item_class=enums.Class(int(payload["classType"])),
1651            sub_type=enums.ItemSubType(int(payload["itemSubType"])),
1652            breaker_type=int(payload["breakerType"]),
1653            default_damagetype=int(payload["defaultDamageType"]),
1654            default_damagetype_hash=default_damagetype_hash,
1655            damagetype_hashes=damagetype_hashes,
1656            tooltip_notifications=payload["tooltipNotifications"],
1657            not_transferable=payload["nonTransferrable"],
1658            allow_actions=payload["allowActions"],
1659            is_equippable=payload["equippable"],
1660            objects=objects,
1661            background_colors=payload.get("backgroundColor", {}),
1662            season_hash=payload.get("seasonHash"),
1663            has_postmaster_effect=payload["doesPostmasterPullHaveSideEffects"],
1664        )
1665
1666    def deserialize_objective_entity(
1667        self, payload: typedefs.JSONObject, /
1668    ) -> entity.ObjectiveEntity:
1669        props = self._set_entity_attrs(payload)
1670        return entity.ObjectiveEntity(
1671            net=self._net,
1672            hash=props.hash,
1673            index=props.index,
1674            description=props.description,
1675            name=props.name,
1676            has_icon=props.has_icon,
1677            icon=props.icon,
1678            unlock_value_hash=payload["unlockValueHash"],
1679            completion_value=payload["completionValue"],
1680            scope=entity.GatingScope(int(payload["scope"])),
1681            location_hash=payload["locationHash"],
1682            allowed_negative_value=payload["allowNegativeValue"],
1683            allowed_value_change=payload["allowValueChangeWhenCompleted"],
1684            counting_downward=payload["isCountingDownward"],
1685            value_style=entity.ValueUIStyle(int(payload["valueStyle"])),
1686            progress_description=payload["progressDescription"],
1687            perks=payload["perks"],
1688            stats=payload["stats"],
1689            minimum_visibility=payload["minimumVisibilityThreshold"],
1690            allow_over_completion=payload["allowOvercompletion"],
1691            show_value_style=payload["showValueOnComplete"],
1692            display_only_objective=payload["isDisplayOnlyObjective"],
1693            complete_value_style=entity.ValueUIStyle(
1694                int(payload["completedValueStyle"])
1695            ),
1696            progress_value_style=entity.ValueUIStyle(
1697                int(payload["inProgressValueStyle"])
1698            ),
1699            ui_label=payload["uiLabel"],
1700            ui_style=entity.ObjectiveUIStyle(int(payload["uiStyle"])),
1701        )
1702
1703    def _deserialize_activity_values(
1704        self, payload: typedefs.JSONObject, /
1705    ) -> activity.ActivityValues:
1706        team: int | None = None
1707        if raw_team := payload.get("team"):
1708            team = raw_team["basic"]["value"]
1709        return activity.ActivityValues(
1710            assists=payload["assists"]["basic"]["value"],
1711            deaths=payload["deaths"]["basic"]["value"],
1712            kills=payload["kills"]["basic"]["value"],
1713            is_completed=bool(payload["completed"]["basic"]["value"]),
1714            opponents_defeated=payload["opponentsDefeated"]["basic"]["value"],
1715            efficiency=payload["efficiency"]["basic"]["value"],
1716            kd_ratio=payload["killsDeathsRatio"]["basic"]["value"],
1717            kd_assists=payload["killsDeathsAssists"]["basic"]["value"],
1718            score=payload["score"]["basic"]["value"],
1719            duration=payload["activityDurationSeconds"]["basic"]["displayValue"],
1720            team=team,
1721            completion_reason=payload["completionReason"]["basic"]["displayValue"],
1722            fireteam_id=payload["fireteamId"]["basic"]["value"],
1723            start_seconds=payload["startSeconds"]["basic"]["value"],
1724            played_time=payload["timePlayedSeconds"]["basic"]["displayValue"],
1725            player_count=payload["playerCount"]["basic"]["value"],
1726            team_score=payload["teamScore"]["basic"]["value"],
1727        )
1728
1729    def deserialize_activity(
1730        self,
1731        payload: typedefs.JSONObject,
1732        /,
1733    ) -> activity.Activity:
1734        period = time.clean_date(payload["period"])
1735        details = payload["activityDetails"]
1736        ref_id = int(details["referenceId"])
1737        instance_id = int(details["instanceId"])
1738        mode = enums.GameMode(details["mode"])
1739        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1740        is_private = details["isPrivate"]
1741        membership_type = enums.MembershipType(int(details["membershipType"]))
1742
1743        # Since we're using the same fields for post activity method
1744        # this check is required since post activity doesn't values values
1745        values = self._deserialize_activity_values(payload["values"])
1746
1747        return activity.Activity(
1748            net=self._net,
1749            hash=ref_id,
1750            instance_id=instance_id,
1751            mode=mode,
1752            modes=modes,
1753            is_private=is_private,
1754            membership_type=membership_type,
1755            occurred_at=period,
1756            values=values,
1757        )
1758
1759    def deserialize_activities(
1760        self, payload: typedefs.JSONObject
1761    ) -> iterators.Iterator[activity.Activity]:
1762        return iterators.Iterator(
1763            [
1764                self.deserialize_activity(activity_)
1765                for activity_ in payload["activities"]
1766            ]
1767        )
1768
1769    def deserialize_extended_weapon_values(
1770        self, payload: typedefs.JSONObject
1771    ) -> activity.ExtendedWeaponValues:
1772        assists: int | None = None
1773        if raw_assists := payload["values"].get("uniqueWeaponAssists"):
1774            assists = raw_assists["basic"]["value"]
1775        assists_damage: int | None = None
1776
1777        if raw_assists_damage := payload["values"].get("uniqueWeaponAssistDamage"):
1778            assists_damage = raw_assists_damage["basic"]["value"]
1779
1780        return activity.ExtendedWeaponValues(
1781            reference_id=int(payload["referenceId"]),
1782            kills=payload["values"]["uniqueWeaponKills"]["basic"]["value"],
1783            precision_kills=payload["values"]["uniqueWeaponPrecisionKills"]["basic"][
1784                "value"
1785            ],
1786            assists=assists,
1787            assists_damage=assists_damage,
1788            precision_kills_percentage=(
1789                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"]["value"],
1790                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"][
1791                    "displayValue"
1792                ],
1793            ),
1794        )
1795
1796    def _deserialize_extended_values(
1797        self, payload: typedefs.JSONObject
1798    ) -> activity.ExtendedValues:
1799        if raw_weapons := payload.get("weapons"):
1800            weapons = [
1801                self.deserialize_extended_weapon_values(value) for value in raw_weapons
1802            ]
1803        else:
1804            weapons = []
1805
1806        return activity.ExtendedValues(
1807            precision_kills=payload["values"]["precisionKills"]["basic"]["value"],
1808            grenade_kills=payload["values"]["weaponKillsGrenade"]["basic"]["value"],
1809            melee_kills=payload["values"]["weaponKillsMelee"]["basic"]["value"],
1810            super_kills=payload["values"]["weaponKillsSuper"]["basic"]["value"],
1811            ability_kills=payload["values"]["weaponKillsAbility"]["basic"]["value"],
1812            weapons=weapons,
1813        )
1814
1815    def deserialize_post_activity_player(
1816        self, payload: typedefs.JSONObject, /
1817    ) -> activity.PostActivityPlayer:
1818        player = payload["player"]
1819
1820        class_hash: int | None = None
1821        if (class_hash := player.get("classHash")) is not None:
1822            class_hash = class_hash
1823
1824        race_hash: int | None = None
1825        if (race_hash := player.get("raceHash")) is not None:
1826            race_hash = race_hash
1827
1828        gender_hash: int | None = None
1829        if (gender_hash := player.get("genderHash")) is not None:
1830            gender_hash = gender_hash
1831
1832        character_class: str | None = None
1833        if character_class := player.get("characterClass"):
1834            character_class = character_class
1835
1836        character_level: int | None = None
1837        if (character_level := player.get("characterLevel")) is not None:
1838            character_level = character_level
1839
1840        return activity.PostActivityPlayer(
1841            standing=int(payload["standing"]),
1842            score=int(payload["score"]["basic"]["value"]),
1843            character_id=payload["characterId"],
1844            destiny_user=self.deserialize_destiny_membership(player["destinyUserInfo"]),
1845            character_class=character_class,
1846            character_level=character_level,
1847            race_hash=race_hash,
1848            gender_hash=gender_hash,
1849            class_hash=class_hash,
1850            light_level=int(player["lightLevel"]),
1851            emblem_hash=int(player["emblemHash"]),
1852            values=self._deserialize_activity_values(payload["values"]),
1853            extended_values=self._deserialize_extended_values(payload["extended"]),
1854        )
1855
1856    def _deserialize_post_activity_team(
1857        self, payload: typedefs.JSONObject
1858    ) -> activity.PostActivityTeam:
1859        return activity.PostActivityTeam(
1860            id=payload["teamId"],
1861            is_defeated=bool(payload["standing"]["basic"]["value"]),
1862            score=int(payload["score"]["basic"]["value"]),
1863            name=payload["teamName"],
1864        )
1865
1866    def deserialize_post_activity(
1867        self, payload: typedefs.JSONObject
1868    ) -> activity.PostActivity:
1869        period = time.clean_date(payload["period"])
1870        details = payload["activityDetails"]
1871        ref_id = int(details["referenceId"])
1872        instance_id = int(details["instanceId"])
1873        mode = enums.GameMode(details["mode"])
1874        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1875        is_private = details["isPrivate"]
1876        membership_type = enums.MembershipType(int(details["membershipType"]))
1877        return activity.PostActivity(
1878            net=self._net,
1879            hash=ref_id,
1880            membership_type=membership_type,
1881            instance_id=instance_id,
1882            mode=mode,
1883            modes=modes,
1884            is_private=is_private,
1885            occurred_at=period,
1886            starting_phase=int(payload["startingPhaseIndex"]),
1887            players=[
1888                self.deserialize_post_activity_player(player)
1889                for player in payload["entries"]
1890            ],
1891            teams=[
1892                self._deserialize_post_activity_team(team) for team in payload["teams"]
1893            ],
1894        )
1895
1896    def _deserialize_aggregated_activity_values(
1897        self, payload: typedefs.JSONObject
1898    ) -> activity.AggregatedActivityValues:
1899        # This ID is always the same for all aggregated values.
1900        activity_id = int(payload["fastestCompletionMsForActivity"]["activityId"])
1901
1902        return activity.AggregatedActivityValues(
1903            id=activity_id,
1904            fastest_completion_time=(
1905                int(payload["fastestCompletionMsForActivity"]["basic"]["value"]),
1906                payload["fastestCompletionMsForActivity"]["basic"]["displayValue"],
1907            ),
1908            completions=int(payload["activityCompletions"]["basic"]["value"]),
1909            kills=int(payload["activityKills"]["basic"]["value"]),
1910            deaths=int(payload["activityDeaths"]["basic"]["value"]),
1911            assists=int(payload["activityAssists"]["basic"]["value"]),
1912            seconds_played=(
1913                int(payload["activitySecondsPlayed"]["basic"]["value"]),
1914                payload["activitySecondsPlayed"]["basic"]["displayValue"],
1915            ),
1916            wins=int(payload["activityWins"]["basic"]["value"]),
1917            goals_missed=int(payload["activityGoalsMissed"]["basic"]["value"]),
1918            special_actions=int(payload["activitySpecialActions"]["basic"]["value"]),
1919            best_goals_hit=int(payload["activityBestGoalsHit"]["basic"]["value"]),
1920            best_single_score=int(
1921                payload["activityBestSingleGameScore"]["basic"]["value"]
1922            ),
1923            goals_hit=int(payload["activityGoalsHit"]["basic"]["value"]),
1924            special_score=int(payload["activitySpecialScore"]["basic"]["value"]),
1925            kd_assists=int(payload["activityKillsDeathsAssists"]["basic"]["value"]),
1926            kd_ratio=float(
1927                payload["activityKillsDeathsAssists"]["basic"]["displayValue"]
1928            ),
1929            precision_kills=int(payload["activityPrecisionKills"]["basic"]["value"]),
1930        )
1931
1932    def deserialize_aggregated_activity(
1933        self, payload: typedefs.JSONObject
1934    ) -> activity.AggregatedActivity:
1935        return activity.AggregatedActivity(
1936            hash=int(payload["activityHash"]),
1937            values=self._deserialize_aggregated_activity_values(payload["values"]),
1938        )
1939
1940    def deserialize_aggregated_activities(
1941        self, payload: typedefs.JSONObject
1942    ) -> iterators.Iterator[activity.AggregatedActivity]:
1943        return iterators.Iterator(
1944            [
1945                self.deserialize_aggregated_activity(activity)
1946                for activity in payload["activities"]
1947            ]
1948        )
1949
1950    def deserialize_linked_profiles(
1951        self, payload: typedefs.JSONObject
1952    ) -> profile.LinkedProfile:
1953        bungie_user = self.deserialize_partial_bungie_user(payload["bnetMembership"])
1954        error_profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
1955        profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
1956
1957        if raw_profile := payload.get("profiles"):
1958            for pfile in raw_profile:
1959                profiles_vec.append(self.deserialize_destiny_membership(pfile))
1960
1961        if raw_profiles_with_errors := payload.get("profilesWithErrors"):
1962            for raw_error_pfile in raw_profiles_with_errors:
1963                if error_pfile := raw_error_pfile.get("infoCard"):
1964                    error_profiles_vec.append(
1965                        self.deserialize_destiny_membership(error_pfile)
1966                    )
1967
1968        return profile.LinkedProfile(
1969            bungie_user=bungie_user,
1970            profiles=profiles_vec,
1971            profiles_with_errors=error_profiles_vec,
1972        )
1973
1974    def deserialize_clan_banners(
1975        self, payload: typedefs.JSONObject
1976    ) -> collections.Sequence[clans.ClanBanner]:
1977        banners_seq: typing.MutableSequence[clans.ClanBanner] = []
1978        if banners := payload.get("clanBannerDecals"):
1979            for k, v in banners.items():
1980                banner_obj = clans.ClanBanner(
1981                    id=int(k),
1982                    foreground=assets.Image(v["foregroundPath"]),
1983                    background=assets.Image(v["backgroundPath"]),
1984                )
1985                banners_seq.append(banner_obj)
1986        return banners_seq
1987
1988    def deserialize_public_milestone_content(
1989        self, payload: typedefs.JSONObject
1990    ) -> milestones.MilestoneContent:
1991        items_categories: milestones.MilestoneItems | None = None
1992
1993        if raw_categories := payload.get("itemCategories"):
1994            for item in raw_categories:
1995                title: str | None = None
1996                if raw_title := item.get("title"):
1997                    title = raw_title
1998                if raw_hashes := item.get("itemHashes"):
1999                    hashes = raw_hashes
2000
2001                items_categories = milestones.MilestoneItems(title=title, hashes=hashes)
2002
2003        tips: typing.MutableSequence[str] = []
2004        if raw_tips := payload.get("tips"):
2005            tips = raw_tips
2006
2007        return milestones.MilestoneContent(
2008            about=typedefs.unknown(payload["about"]),
2009            status=typedefs.unknown(payload["status"]),
2010            tips=tips,
2011            items=items_categories,
2012        )
2013
2014    def deserialize_friend(self, payload: typedefs.JSONObject, /) -> friends.Friend:
2015        bungie_user: user.BungieUser | None = None
2016
2017        if raw_bungie_user := payload.get("bungieNetUser"):
2018            bungie_user = self.deserialize_bungie_user(raw_bungie_user)
2019
2020        return friends.Friend(
2021            net=self._net,
2022            id=int(payload["lastSeenAsMembershipId"]),
2023            name=typedefs.unknown(payload["bungieGlobalDisplayName"]),
2024            code=payload.get("bungieGlobalDisplayNameCode"),
2025            relationship=enums.Relationship(payload["relationship"]),
2026            user=bungie_user,
2027            online_status=enums.Presence(payload["onlineStatus"]),
2028            online_title=payload["onlineTitle"],
2029            type=enums.MembershipType(payload["lastSeenAsBungieMembershipType"]),
2030        )
2031
2032    def deserialize_friends(
2033        self, payload: typedefs.JSONObject
2034    ) -> collections.Sequence[friends.Friend]:
2035        mut_seq: typing.MutableSequence[friends.Friend] = []
2036        if raw_friends := payload.get("friends"):
2037            for friend in raw_friends:
2038                mut_seq.append(self.deserialize_friend(friend))
2039        return mut_seq
2040
2041    def deserialize_friend_requests(
2042        self, payload: typedefs.JSONObject
2043    ) -> friends.FriendRequestView:
2044        incoming: typing.MutableSequence[friends.Friend] = []
2045        outgoing: typing.MutableSequence[friends.Friend] = []
2046
2047        if raw_incoming_requests := payload.get("incomingRequests"):
2048            for incoming_request in raw_incoming_requests:
2049                incoming.append(self.deserialize_friend(incoming_request))
2050
2051        if raw_outgoing_requests := payload.get("outgoingRequests"):
2052            for outgoing_request in raw_outgoing_requests:
2053                outgoing.append(self.deserialize_friend(outgoing_request))
2054
2055        return friends.FriendRequestView(incoming=incoming, outgoing=outgoing)
2056
2057    def _set_fireteam_fields(
2058        self, payload: typedefs.JSONObject, total_results: int | None = None
2059    ) -> fireteams.Fireteam:
2060        activity_type = fireteams.FireteamActivity(payload["activityType"])
2061        return fireteams.Fireteam(
2062            id=int(payload["fireteamId"]),
2063            group_id=int(payload["groupId"]),
2064            platform=fireteams.FireteamPlatform(payload["platform"]),
2065            is_immediate=payload["isImmediate"],
2066            activity_type=activity_type,
2067            owner_id=int(payload["ownerMembershipId"]),
2068            player_slot_count=payload["playerSlotCount"],
2069            available_player_slots=payload["availablePlayerSlotCount"],
2070            available_alternate_slots=payload["availableAlternateSlotCount"],
2071            title=payload["title"],
2072            date_created=time.clean_date(payload["dateCreated"]),
2073            is_public=payload["isPublic"],
2074            locale=fireteams.FireteamLanguage(payload["locale"]),
2075            is_valid=payload["isValid"],
2076            last_modified=time.clean_date(payload["datePlayerModified"]),
2077            date_modified=time.clean_date(payload["dateModified"])
2078            if "dateModified" in payload
2079            else None,
2080            scheduled_time=time.clean_date(payload["scheduledTime"])
2081            if "scheduledTime" in payload
2082            else None,
2083            total_results=total_results or 0,
2084        )
2085
2086    def deserialize_fireteams(
2087        self, payload: typedefs.JSONObject
2088    ) -> collections.Sequence[fireteams.Fireteam]:
2089        if "results" in payload:
2090            fireteams_ = [
2091                self._set_fireteam_fields(
2092                    elem, total_results=int(payload["totalResults"])
2093                )
2094                for elem in payload["results"]
2095            ]
2096        else:
2097            fireteams_ = []
2098        return fireteams_
2099
2100    def deserialize_fireteam_destiny_users(
2101        self, payload: typedefs.JSONObject
2102    ) -> fireteams.FireteamUser:
2103        destiny_obj = self.deserialize_destiny_membership(payload)
2104        return fireteams.FireteamUser(
2105            net=self._net,
2106            id=destiny_obj.id,
2107            code=destiny_obj.code,
2108            icon=destiny_obj.icon,
2109            types=destiny_obj.types,
2110            type=destiny_obj.type,
2111            is_public=destiny_obj.is_public,
2112            crossave_override=destiny_obj.crossave_override,
2113            name=destiny_obj.name,
2114            last_seen_name=destiny_obj.last_seen_name,
2115            fireteam_display_name=payload["FireteamDisplayName"],
2116            fireteam_membership_id=enums.MembershipType(
2117                payload["FireteamMembershipType"]
2118            ),
2119        )
2120
2121    def deserialize_fireteam_members(
2122        self, payload: typedefs.JSONObject, *, alternatives: bool = False
2123    ) -> collections.Sequence[fireteams.FireteamMember]:
2124        members_: list[fireteams.FireteamMember] = []
2125        if members := payload.get("Members" if not alternatives else "Alternates"):
2126            for member in members:
2127                bungie_fields = self.deserialize_partial_bungie_user(member)
2128                members_fields = fireteams.FireteamMember(
2129                    destiny_user=self.deserialize_fireteam_destiny_users(member),
2130                    has_microphone=member["hasMicrophone"],
2131                    character_id=int(member["characterId"]),
2132                    date_joined=time.clean_date(member["dateJoined"]),
2133                    last_platform_invite_date=time.clean_date(
2134                        member["lastPlatformInviteAttemptDate"]
2135                    ),
2136                    last_platform_invite_result=int(
2137                        member["lastPlatformInviteAttemptResult"]
2138                    ),
2139                    net=self._net,
2140                    name=bungie_fields.name,
2141                    id=bungie_fields.id,
2142                    icon=bungie_fields.icon,
2143                    is_public=bungie_fields.is_public,
2144                    crossave_override=bungie_fields.crossave_override,
2145                    types=bungie_fields.types,
2146                    type=bungie_fields.type,
2147                )
2148                members_.append(members_fields)
2149        return members_
2150
2151    def deserialize_available_fireteam(
2152        self, payload: typedefs.JSONObject
2153    ) -> fireteams.AvailableFireteam:
2154        fields = self._set_fireteam_fields(payload["Summary"])
2155        return fireteams.AvailableFireteam(
2156            id=fields.id,
2157            group_id=fields.group_id,
2158            platform=fields.platform,
2159            activity_type=fields.activity_type,
2160            is_immediate=fields.is_immediate,
2161            is_public=fields.is_public,
2162            is_valid=fields.is_valid,
2163            owner_id=fields.owner_id,
2164            player_slot_count=fields.player_slot_count,
2165            available_player_slots=fields.available_player_slots,
2166            available_alternate_slots=fields.available_alternate_slots,
2167            title=fields.title,
2168            date_created=fields.date_created,
2169            locale=fields.locale,
2170            last_modified=fields.last_modified,
2171            total_results=fields.total_results,
2172            scheduled_time=fields.scheduled_time,
2173            date_modified=fields.date_modified,
2174            members=self.deserialize_fireteam_members(payload),
2175            alternatives=self.deserialize_fireteam_members(payload, alternatives=True),
2176        )
2177
2178    def deserialize_available_fireteams(
2179        self, data: typedefs.JSONObject
2180    ) -> collections.Sequence[fireteams.AvailableFireteam]:
2181        if raw_results := data.get("results"):
2182            fireteam_results: list[fireteams.AvailableFireteam] = [
2183                self.deserialize_available_fireteam(f) for f in raw_results
2184            ]
2185        else:
2186            fireteam_results = []
2187        return fireteam_results
2188
2189    def deserialize_fireteam_party(
2190        self, payload: typedefs.JSONObject
2191    ) -> fireteams.FireteamParty:
2192        last_destination_hash: int | None = None
2193        if raw_dest_hash := payload.get("lastOrbitedDestinationHash"):
2194            last_destination_hash = int(raw_dest_hash)
2195
2196        return fireteams.FireteamParty(
2197            members=[
2198                self._deserialize_fireteam_party_member(member)
2199                for member in payload["partyMembers"]
2200            ],
2201            activity=self._deserialize_fireteam_party_current_activity(
2202                payload["currentActivity"]
2203            ),
2204            settings=self._deserialize_fireteam_party_settings(payload["joinability"]),
2205            last_destination_hash=last_destination_hash,
2206            tracking=payload["tracking"],
2207        )
2208
2209    def _deserialize_fireteam_party_member(
2210        self, payload: typedefs.JSONObject
2211    ) -> fireteams.FireteamPartyMember:
2212        status = fireteams.FireteamPartyMemberState(payload["status"])
2213
2214        return fireteams.FireteamPartyMember(
2215            membership_id=int(payload["membershipId"]),
2216            emblem_hash=int(payload["emblemHash"]),
2217            status=status,
2218            display_name=payload["displayName"] if payload["displayName"] else None,
2219        )
2220
2221    def _deserialize_fireteam_party_current_activity(
2222        self, payload: typedefs.JSONObject
2223    ) -> fireteams.FireteamPartyCurrentActivity:
2224        start_date: datetime.datetime | None = None
2225        if raw_start_date := payload.get("startTime"):
2226            start_date = time.clean_date(raw_start_date)
2227
2228        end_date: datetime.datetime | None = None
2229        if raw_end_date := payload.get("endTime"):
2230            end_date = time.clean_date(raw_end_date)
2231        return fireteams.FireteamPartyCurrentActivity(
2232            start_time=start_date,
2233            end_time=end_date,
2234            score=float(payload["score"]),
2235            highest_opposing_score=float(payload["highestOpposingFactionScore"]),
2236            opponents_count=int(payload["numberOfOpponents"]),
2237            player_count=int(payload["numberOfPlayers"]),
2238        )
2239
2240    def _deserialize_fireteam_party_settings(
2241        self, payload: typedefs.JSONObject
2242    ) -> fireteams.FireteamPartySettings:
2243        closed_reasons = enums.ClosedReasons(payload["closedReasons"])
2244        return fireteams.FireteamPartySettings(
2245            open_slots=int(payload["openSlots"]),
2246            privacy_setting=enums.PrivacySetting(int(payload["privacySetting"])),
2247            closed_reasons=closed_reasons,
2248        )
2249
2250    def deserialize_seasonal_artifact(
2251        self, payload: typedefs.JSONObject
2252    ) -> season.Artifact:
2253        if raw_artifact := payload.get("seasonalArtifact"):
2254            if points := raw_artifact.get("pointProgression"):
2255                points_prog = progressions.Progression(
2256                    hash=points["progressionHash"],
2257                    level=points["level"],
2258                    cap=points["levelCap"],
2259                    daily_limit=points["dailyLimit"],
2260                    weekly_limit=points["weeklyLimit"],
2261                    current_progress=points["currentProgress"],
2262                    daily_progress=points["dailyProgress"],
2263                    needed=points["progressToNextLevel"],
2264                    next_level=points["nextLevelAt"],
2265                )
2266
2267            if bonus := raw_artifact.get("powerBonusProgression"):
2268                power_bonus_prog = progressions.Progression(
2269                    hash=bonus["progressionHash"],
2270                    level=bonus["level"],
2271                    cap=bonus["levelCap"],
2272                    daily_limit=bonus["dailyLimit"],
2273                    weekly_limit=bonus["weeklyLimit"],
2274                    current_progress=bonus["currentProgress"],
2275                    daily_progress=bonus["dailyProgress"],
2276                    needed=bonus["progressToNextLevel"],
2277                    next_level=bonus["nextLevelAt"],
2278                )
2279            artifact = season.Artifact(
2280                hash=raw_artifact["artifactHash"],
2281                power_bonus=raw_artifact["powerBonus"],
2282                acquired_points=raw_artifact["pointsAcquired"],
2283                bonus=power_bonus_prog,
2284                points=points_prog,
2285            )
2286        return artifact
2287
2288    def deserialize_profile_progression(
2289        self, payload: typedefs.JSONObject
2290    ) -> profile.ProfileProgression:
2291        return profile.ProfileProgression(
2292            artifact=self.deserialize_seasonal_artifact(payload["data"]),
2293            checklist={
2294                int(check_id): checklists
2295                for check_id, checklists in payload["data"]["checklists"].items()
2296            },
2297        )
2298
2299    def deserialize_instanced_item(
2300        self, payload: typedefs.JSONObject
2301    ) -> items.ItemInstance:
2302        damage_type_hash: int | None = None
2303        if raw_damagetype_hash := payload.get("damageTypeHash"):
2304            damage_type_hash = int(raw_damagetype_hash)
2305
2306        required_hashes: typing.Optional[collections.Collection[int]] = None
2307        if raw_required_hashes := payload.get("unlockHashesRequiredToEquip"):
2308            required_hashes = [int(raw_hash) for raw_hash in raw_required_hashes]
2309
2310        breaker_type: items.ItemBreakerType | None = None
2311        if raw_break_type := payload.get("breakerType"):
2312            breaker_type = items.ItemBreakerType(int(raw_break_type))
2313
2314        breaker_type_hash: int | None = None
2315        if raw_break_type_hash := payload.get("breakerTypeHash"):
2316            breaker_type_hash = int(raw_break_type_hash)
2317
2318        energy: items.ItemEnergy | None = None
2319        if raw_energy := payload.get("energy"):
2320            energy = self.deserialize_item_energy(raw_energy)
2321
2322        primary_stats = None
2323        if raw_primary_stats := payload.get("primaryStat"):
2324            primary_stats = self.deserialize_item_stats_view(raw_primary_stats)
2325
2326        return items.ItemInstance(
2327            damage_type=enums.DamageType(int(payload["damageType"])),
2328            damage_type_hash=damage_type_hash,
2329            primary_stat=primary_stats,
2330            item_level=int(payload["itemLevel"]),
2331            quality=int(payload["quality"]),
2332            is_equipped=payload["isEquipped"],
2333            can_equip=payload["canEquip"],
2334            equip_required_level=int(payload["equipRequiredLevel"]),
2335            required_equip_unlock_hashes=required_hashes,
2336            cant_equip_reason=int(payload["cannotEquipReason"]),
2337            breaker_type=breaker_type,
2338            breaker_type_hash=breaker_type_hash,
2339            energy=energy,
2340        )
2341
2342    def deserialize_item_energy(self, payload: typedefs.JSONObject) -> items.ItemEnergy:
2343        energy_hash: int | None = None
2344        if raw_energy_hash := payload.get("energyTypeHash"):
2345            energy_hash = int(raw_energy_hash)
2346
2347        return items.ItemEnergy(
2348            hash=energy_hash,
2349            type=items.ItemEnergyType(int(payload["energyType"])),
2350            capacity=int(payload["energyCapacity"]),
2351            used_energy=int(payload["energyUsed"]),
2352            unused_energy=int(payload["energyUnused"]),
2353        )
2354
2355    def deserialize_item_perk(self, payload: typedefs.JSONObject) -> items.ItemPerk:
2356        perk_hash: int | None = None
2357        if raw_perk_hash := payload.get("perkHash"):
2358            perk_hash = int(raw_perk_hash)
2359
2360        return items.ItemPerk(
2361            hash=perk_hash,
2362            icon=assets.Image(payload["iconPath"]),
2363            is_active=payload["isActive"],
2364            is_visible=payload["visible"],
2365        )
2366
2367    def deserialize_item_socket(self, payload: typedefs.JSONObject) -> items.ItemSocket:
2368        plug_hash: int | None = None
2369        if raw_plug_hash := payload.get("plugHash"):
2370            plug_hash = int(raw_plug_hash)
2371
2372        enable_fail_indexes: list[int] | None = None
2373        if raw_indexes := payload.get("enableFailIndexes"):
2374            enable_fail_indexes = [int(index) for index in raw_indexes]
2375
2376        return items.ItemSocket(
2377            plug_hash=plug_hash,
2378            is_enabled=payload["isEnabled"],
2379            enable_fail_indexes=enable_fail_indexes,
2380            is_visible=payload.get("visible"),
2381        )
2382
2383    def deserialize_item_stats_view(
2384        self, payload: typedefs.JSONObject
2385    ) -> items.ItemStatsView:
2386        return items.ItemStatsView(
2387            stat_hash=payload.get("statHash"), value=payload.get("value")
2388        )
2389
2390    def deserialize_plug_item_state(
2391        self, payload: typedefs.JSONObject
2392    ) -> items.PlugItemState:
2393        item_hash: int | None = None
2394        if raw_item_hash := payload.get("plugItemHash"):
2395            item_hash = int(raw_item_hash)
2396
2397        insert_fail_indexes: list[int] | None = None
2398        if raw_fail_indexes := payload.get("insertFailIndexes"):
2399            insert_fail_indexes = [int(k) for k in raw_fail_indexes]
2400
2401        enable_fail_indexes: list[int] | None = None
2402        if raw_enabled_indexes := payload.get("enableFailIndexes"):
2403            enable_fail_indexes = [int(k) for k in raw_enabled_indexes]
2404
2405        return items.PlugItemState(
2406            item_hash=item_hash,
2407            insert_fail_indexes=insert_fail_indexes,
2408            enable_fail_indexes=enable_fail_indexes,
2409            is_enabled=payload["enabled"],
2410            can_insert=payload["canInsert"],
2411        )

The base deserialization factory class for all aiobungie objects.

This entity factory is used to deserialize JSON responses from the REST client and turning them into a aiobungie.crates Python classes.

Factory(net: aiobungie.traits.Netrunner)
69    def __init__(self, net: traits.Netrunner) -> None:
70        self._net = net
def deserialize_bungie_user( self, data: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.user.BungieUser:
72    def deserialize_bungie_user(self, data: typedefs.JSONObject) -> user.BungieUser:
73        return user.BungieUser(
74            id=int(data["membershipId"]),
75            created_at=time.clean_date(data["firstAccess"]),
76            name=data.get("cachedBungieGlobalDisplayName"),
77            is_deleted=data["isDeleted"],
78            about=data["about"],
79            updated_at=time.clean_date(data["lastUpdate"]),
80            psn_name=data.get("psnDisplayName", None),
81            stadia_name=data.get("stadiaDisplayName", None),
82            steam_name=data.get("steamDisplayName", None),
83            twitch_name=data.get("twitchDisplayName", None),
84            blizzard_name=data.get("blizzardDisplayName", None),
85            status=data["statusText"],
86            locale=data["locale"],
87            picture=assets.Image(path=str(data["profilePicturePath"])),
88            code=data.get("cachedBungieGlobalDisplayNameCode", None),
89            unique_name=data.get("uniqueName", None),
90            theme_id=int(data["profileTheme"]),
91            show_activity=bool(data["showActivity"]),
92            theme_name=data["profileThemeName"],
93            display_title=data["userTitleDisplay"],
94        )

Deserialize a raw JSON Bungie.net user only payload into a user object.

This only returns the Bungie.net user and not the Destiny memberships.

Parameters
Returns
def deserialize_partial_bungie_user( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.user.PartialBungieUser:
 96    def deserialize_partial_bungie_user(
 97        self, payload: typedefs.JSONObject
 98    ) -> user.PartialBungieUser:
 99        return user.PartialBungieUser(
100            net=self._net,
101            types=[
102                enums.MembershipType(type_)
103                for type_ in payload.get("applicableMembershipTypes", [])
104            ],
105            name=payload.get("displayName"),
106            id=int(payload["membershipId"]),
107            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
108            is_public=payload["isPublic"],
109            icon=assets.Image(payload.get("iconPath", "")),
110            type=enums.MembershipType(payload["membershipType"]),
111        )

Deserialize a raw JSON of a partial bungieNetUserInfo.

A partial user is a bungie.net user payload with missing information from the main BungieUser object.

Parameters
Returns
def deserialize_destiny_membership( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.user.DestinyMembership:
113    def deserialize_destiny_membership(
114        self, payload: typedefs.JSONObject
115    ) -> user.DestinyMembership:
116        name: str | None = None
117        if (raw_name := payload.get("bungieGlobalDisplayName")) is not None:
118            name = typedefs.unknown(raw_name)
119
120        return user.DestinyMembership(
121            net=self._net,
122            id=int(payload["membershipId"]),
123            name=name,
124            code=payload.get("bungieGlobalDisplayNameCode", None),
125            last_seen_name=payload.get("LastSeenDisplayName")
126            or payload.get("displayName")  # noqa: W503
127            or "",  # noqa: W503
128            type=enums.MembershipType(payload["membershipType"]),
129            is_public=payload["isPublic"],
130            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
131            icon=assets.Image(payload.get("iconPath", "")),
132            types=[
133                enums.MembershipType(type_)
134                for type_ in payload.get("applicableMembershipTypes", [])
135            ],
136        )

Deserialize a raw JSON of destinyUserInfo destiny membership information.

Parameters
Returns
  • aiobungie.crates.user.DestinyMembership: A Destiny 2 membership.
def deserialize_destiny_memberships( self, data: collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]) -> collections.abc.Sequence[aiobungie.crates.user.DestinyMembership]:
138    def deserialize_destiny_memberships(
139        self, data: typedefs.JSONArray
140    ) -> collections.Sequence[user.DestinyMembership]:
141        return [self.deserialize_destiny_membership(membership) for membership in data]

Deserialize a raw JSON payload/array of destinyUserInfo.

Parameters
Returns
  • collections.Sequence[aiobungie.crates.user.DestinyMembership]: A sequence of Destiny 2 memberships.
def deserialize_user( self, data: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.user.User:
143    def deserialize_user(self, data: typedefs.JSONObject) -> user.User:
144        primary_membership_id: int | None = None
145        if raw_primary_id := data.get("primaryMembershipId"):
146            primary_membership_id = int(raw_primary_id)
147
148        return user.User(
149            bungie_user=self.deserialize_bungie_user(data["bungieNetUser"]),
150            memberships=self.deserialize_destiny_memberships(
151                data["destinyMemberships"]
152            ),
153            primary_membership_id=primary_membership_id,
154        )

Deserialize a raw JSON results of fetched user memberships and Bungie.net user its their id.

Parameters
Returns
def deserialize_searched_user( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.user.SearchableDestinyUser:
156    def deserialize_searched_user(
157        self, payload: typedefs.JSONObject
158    ) -> user.SearchableDestinyUser:
159        code: int | None = None
160        if raw_code := payload.get("bungieGlobalDisplayNameCode"):
161            code = int(raw_code)
162
163        bungie_id: int | None = None
164        if raw_bungie_id := payload.get("bungieNetMembershipId"):
165            bungie_id = int(raw_bungie_id)
166
167        return user.SearchableDestinyUser(
168            name=typedefs.unknown(payload["bungieGlobalDisplayName"]),
169            code=code,
170            bungie_id=bungie_id,
171            memberships=self.deserialize_destiny_memberships(
172                payload["destinyMemberships"]
173            ),
174        )

Deserialize the results of user search details.

Parameters
Returns
def deserialize_user_credentials( self, payload: collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]) -> collections.abc.Sequence[aiobungie.crates.user.UserCredentials]:
176    def deserialize_user_credentials(
177        self, payload: typedefs.JSONArray
178    ) -> collections.Sequence[user.UserCredentials]:
179        return [
180            user.UserCredentials(
181                type=enums.CredentialType(int(creds["credentialType"])),
182                display_name=creds["credentialDisplayName"],
183                is_public=creds["isPublic"],
184                self_as_string=creds.get("credentialAsString"),
185            )
186            for creds in payload
187        ]

Deserialize a JSON array of Bungie user credentials.

Parameters
Returns
def deserialize_user_themes( self, payload: collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]) -> collections.abc.Sequence[aiobungie.crates.user.UserThemes]:
189    def deserialize_user_themes(
190        self, payload: typedefs.JSONArray
191    ) -> collections.Sequence[user.UserThemes]:
192        return [
193            user.UserThemes(
194                id=int(entry["userThemeId"]),
195                name=entry["userThemeName"] if "userThemeName" in entry else None,
196                description=entry["userThemeDescription"]
197                if "userThemeDescription" in entry
198                else None,
199            )
200            for entry in payload
201        ]

Deserialize a raw JSON array of Bungie user themes.

Parameters
Returns
  • collections.Sequence[aiobungie.crates.user.UserThemes]: A sequence of bungie user themes.
def deserialize_clan( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.clans.Clan:
203    def deserialize_clan(self, payload: typedefs.JSONObject) -> clans.Clan:
204        # This is kinda redundant
205        data = payload
206
207        # This is always outside the details.
208        current_user_map: collections.Mapping[str, clans.ClanMember] = {}
209        if raw_current_user_map := payload.get("currentUserMemberMap"):
210            current_user_map = {
211                membership_type: self.deserialize_clan_member(membership)
212                for membership_type, membership in raw_current_user_map.items()
213            }
214
215        try:
216            data = payload["detail"]
217        except KeyError:
218            pass
219
220        id = data["groupId"]
221        name = data["name"]
222        created_at = data["creationDate"]
223        member_count = data["memberCount"]
224        about = data["about"]
225        motto = data["motto"]
226        is_public = data["isPublic"]
227        banner = assets.Image(str(data["bannerPath"]))
228        avatar = assets.Image(str(data["avatarPath"]))
229        tags = data["tags"]
230        type = data["groupType"]
231
232        features = data["features"]
233        features_obj = clans.ClanFeatures(
234            max_members=features["maximumMembers"],
235            max_membership_types=features["maximumMembershipsOfGroupType"],
236            capabilities=features["capabilities"],
237            membership_types=features["membershipTypes"],
238            invite_permissions=features["invitePermissionOverride"],
239            update_banner_permissions=features["updateBannerPermissionOverride"],
240            update_culture_permissions=features["updateCulturePermissionOverride"],
241            join_level=features["joinLevel"],
242        )
243
244        information: typedefs.JSONObject = data["clanInfo"]
245        progression: collections.Mapping[int, progressions.Progression] = {
246            int(prog_hash): self.deserialize_progressions(prog)
247            for prog_hash, prog in information["d2ClanProgressions"].items()
248        }
249
250        return clans.Clan(
251            net=self._net,
252            id=int(id),
253            name=name,
254            type=enums.GroupType(type),
255            created_at=time.clean_date(created_at),
256            member_count=member_count,
257            motto=motto,
258            about=about,
259            is_public=is_public,
260            banner=banner,
261            avatar=avatar,
262            tags=tags,
263            features=features_obj,
264            owner=self.deserialize_clan_member(payload["founder"])
265            if "founder" in payload
266            else None,
267            progressions=progression,
268            call_sign=information["clanCallsign"],
269            banner_data=information["clanBannerData"],
270            chat_security=data["chatSecurity"],
271            conversation_id=int(data["conversationId"]),
272            allow_chat=data["allowChat"],
273            theme=data["theme"],
274            current_user_membership=current_user_map,
275        )

Deserialize a raw JSON payload of Bungie clan information.

Parameters
Returns
def deserialize_clan_member( self, data: collections.abc.Mapping[str, typing.Any], /) -> aiobungie.crates.clans.ClanMember:
277    def deserialize_clan_member(self, data: typedefs.JSONObject, /) -> clans.ClanMember:
278        destiny_user = self.deserialize_destiny_membership(data["destinyUserInfo"])
279        return clans.ClanMember(
280            net=self._net,
281            last_seen_name=destiny_user.last_seen_name,
282            id=destiny_user.id,
283            name=destiny_user.name,
284            icon=destiny_user.icon,
285            last_online=time.from_timestamp(int(data["lastOnlineStatusChange"])),
286            group_id=int(data["groupId"]),
287            joined_at=time.clean_date(data["joinDate"]),
288            types=destiny_user.types,
289            is_public=destiny_user.is_public,
290            type=destiny_user.type,
291            code=destiny_user.code,
292            is_online=data["isOnline"],
293            crossave_override=destiny_user.crossave_override,
294            bungie_user=self.deserialize_partial_bungie_user(data["bungieNetUserInfo"])
295            if "bungieNetUserInfo" in data
296            else None,
297            member_type=enums.ClanMemberType(int(data["memberType"])),
298        )

Deserialize a JSON payload of a clan member information.

Parameters
Returns
def deserialize_clan_members( self, data: collections.abc.Mapping[str, typing.Any], /) -> Iterator[aiobungie.crates.clans.ClanMember]:
300    def deserialize_clan_members(
301        self, data: typedefs.JSONObject, /
302    ) -> iterators.Iterator[clans.ClanMember]:
303        return iterators.Iterator(
304            [self.deserialize_clan_member(member) for member in data["results"]]
305        )

Deserialize a JSON payload of a clan members information.

Parameters
Returns
def deserialize_group_member( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.clans.GroupMember:
307    def deserialize_group_member(
308        self, payload: typedefs.JSONObject
309    ) -> clans.GroupMember:
310        member = payload["member"]
311        return clans.GroupMember(
312            net=self._net,
313            join_date=time.clean_date(member["joinDate"]),
314            group_id=int(member["groupId"]),
315            member_type=enums.ClanMemberType(member["memberType"]),
316            is_online=member["isOnline"],
317            last_online=time.from_timestamp(int(member["lastOnlineStatusChange"])),
318            inactive_memberships=payload.get("areAllMembershipsInactive", None),
319            member=self.deserialize_destiny_membership(member["destinyUserInfo"]),
320            group=self.deserialize_clan(payload["group"]),
321        )

Deserialize a JSON payload of group information for a member.

Parameters
Returns
def deserialize_clan_conversations( self, payload: collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]) -> collections.abc.Sequence[aiobungie.crates.clans.ClanConversation]:
335    def deserialize_clan_conversations(
336        self, payload: typedefs.JSONArray
337    ) -> collections.Sequence[clans.ClanConversation]:
338        return [self._deserialize_clan_conversation(conv) for conv in payload]

Deserialize a JSON array of a clan conversations information.

Parameters
Returns
def deserialize_app_owner( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.application.ApplicationOwner:
340    def deserialize_app_owner(
341        self, payload: typedefs.JSONObject
342    ) -> application.ApplicationOwner:
343        return application.ApplicationOwner(
344            net=self._net,
345            name=payload.get("bungieGlobalDisplayName"),
346            id=int(payload["membershipId"]),
347            type=enums.MembershipType(payload["membershipType"]),
348            icon=assets.Image(str(payload["iconPath"])),
349            is_public=payload["isPublic"],
350            code=payload.get("bungieGlobalDisplayNameCode", None),
351        )

Deserialize a JSON payload of Bungie Developer portal application owner information.

Parameters
Returns
  • aiobungie.crates.application.ApplicationOwner: An application owner.
def deserialize_app( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.application.Application:
353    def deserialize_app(self, payload: typedefs.JSONObject) -> application.Application:
354        return application.Application(
355            id=int(payload["applicationId"]),
356            name=payload["name"],
357            link=payload["link"],
358            status=payload["status"],
359            redirect_url=payload.get("redirectUrl", None),
360            created_at=time.clean_date(str(payload["creationDate"])),
361            published_at=time.clean_date(str(payload["firstPublished"])),
362            owner=self.deserialize_app_owner(payload["team"][0]["user"]),  # type: ignore
363            scope=payload.get("scope"),
364        )

Deserialize a JSON payload of Bungie Developer portal application information.

Parameters
Returns
  • aiobungie.crates.application.Application: An application.
def deserialize_profile( self, payload: collections.abc.Mapping[str, typing.Any], /) -> aiobungie.crates.profile.Profile:
390    def deserialize_profile(self, payload: typedefs.JSONObject, /) -> profile.Profile:
391        payload = payload["data"]
392        id = int(payload["userInfo"]["membershipId"])
393        name = payload["userInfo"]["displayName"]
394        is_public = payload["userInfo"]["isPublic"]
395        type = enums.MembershipType(payload["userInfo"]["membershipType"])
396        last_played = time.clean_date(str(payload["dateLastPlayed"]))
397        character_ids = [int(cid) for cid in payload["characterIds"]]
398        power_cap = payload["currentSeasonRewardPowerCap"]
399
400        return profile.Profile(
401            id=int(id),
402            name=name,
403            is_public=is_public,
404            type=type,
405            last_played=last_played,
406            character_ids=character_ids,
407            power_cap=power_cap,
408            net=self._net,
409        )

Deserialize a JSON payload of Bungie.net profile information.

Parameters
Returns
def deserialize_profile_item( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.profile.ProfileItemImpl:
411    def deserialize_profile_item(
412        self, payload: typedefs.JSONObject
413    ) -> profile.ProfileItemImpl:
414        instance_id: int | None = None
415        if raw_instance_id := payload.get("itemInstanceId"):
416            instance_id = int(raw_instance_id)
417
418        version_number: int | None = None
419        if raw_version := payload.get("versionNumber"):
420            version_number = int(raw_version)
421
422        transfer_status = enums.TransferStatus(payload["transferStatus"])
423
424        return profile.ProfileItemImpl(
425            net=self._net,
426            hash=payload["itemHash"],
427            quantity=payload["quantity"],
428            bind_status=enums.ItemBindStatus(payload["bindStatus"]),
429            location=enums.ItemLocation(payload["location"]),
430            bucket=payload["bucketHash"],
431            transfer_status=transfer_status,
432            lockable=payload["lockable"],
433            state=enums.ItemState(payload["state"]),
434            dismantle_permissions=payload["dismantlePermission"],
435            is_wrapper=payload["isWrapper"],
436            instance_id=instance_id,
437            version_number=version_number,
438            ornament_id=payload.get("overrideStyleItemHash"),
439        )

Deserialize a JSON payload of a singular profile component item.

Parameters
Returns
def deserialize_objectives( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.records.Objective:
441    def deserialize_objectives(self, payload: typedefs.JSONObject) -> records.Objective:
442        return records.Objective(
443            net=self._net,
444            hash=payload["objectiveHash"],
445            visible=payload["visible"],
446            complete=payload["complete"],
447            completion_value=payload["completionValue"],
448            progress=payload.get("progress"),
449            destination_hash=payload.get("destinationHash"),
450            activity_hash=payload.get("activityHash"),
451        )

Deserialize a JSON payload of an objective found in a record profile component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
  • aiobungie.crates.records.Objective: A record objective object.
def deserialize_records( self, payload: collections.abc.Mapping[str, typing.Any], scores: aiobungie.crates.records.RecordScores | None = None, **nodes: int) -> aiobungie.crates.records.Record:
454    def deserialize_records(
455        self,
456        payload: typedefs.JSONObject,
457        scores: records.RecordScores | None = None,
458        **nodes: int,
459    ) -> records.Record:
460        objectives: list[records.Objective] | None = None
461        interval_objectives: list[records.Objective] | None = None
462        record_state: records.RecordState | int
463
464        record_state = records.RecordState(payload["state"])
465
466        if raw_objs := payload.get("objectives"):
467            objectives = [self.deserialize_objectives(obj) for obj in raw_objs]
468
469        if raw_interval_objs := payload.get("intervalObjectives"):
470            interval_objectives = [
471                self.deserialize_objectives(obj) for obj in raw_interval_objs
472            ]
473
474        return records.Record(
475            scores=scores,
476            categories_node_hash=nodes.get("categories_hash"),
477            seals_node_hash=nodes.get("seals_hash"),
478            state=record_state,
479            objectives=objectives,
480            interval_objectives=interval_objectives,
481            redeemed_count=payload.get("intervalsRedeemedCount", 0),
482            completion_times=payload.get("completedCount", None),
483            reward_visibility=payload.get("rewardVisibility"),
484        )

Deserialize a JSON object of a profile record component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON object payload
  • scores (records.RecordScores | None): The records scores object. This exists only to keep the signature of aiobungie.crates.CharacterRecord with the record object. As it will always be None in that object.
  • **nodes (int): An int kwargs use to grab the node hashes while deserializing components.
Returns
  • aiobungie.records.Record: A standard implementation of a profile record component.
def deserialize_character_records( self, payload: collections.abc.Mapping[str, typing.Any], scores: aiobungie.crates.records.RecordScores | None = None, record_hashes: list[int] | None = None) -> aiobungie.crates.records.CharacterRecord:
486    def deserialize_character_records(
487        self,
488        payload: typedefs.JSONObject,
489        scores: records.RecordScores | None = None,
490        record_hashes: list[int] | None = None,
491    ) -> records.CharacterRecord:
492        record = self.deserialize_records(payload, scores)
493        return records.CharacterRecord(
494            scores=scores,
495            categories_node_hash=record.categories_node_hash,
496            seals_node_hash=record.seals_node_hash,
497            state=record.state,
498            objectives=record.objectives,
499            interval_objectives=record.interval_objectives,
500            redeemed_count=payload.get("intervalsRedeemedCount", 0),
501            completion_times=payload.get("completedCount"),
502            reward_visibility=payload.get("rewardVisibility"),
503            record_hashes=record_hashes or [],
504        )

Deserialize a JSON object of a profile character record component.

This almost does the same this as deserialize_records but has more fields which can only be found in a character record.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON object payload
Returns
  • aiobungie.records.CharacterRecord: A standard implementation of a profile character record component.
def deserialize_character_dye( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.character.Dye:
506    def deserialize_character_dye(self, payload: typedefs.JSONObject) -> character.Dye:
507        return character.Dye(
508            channel_hash=payload["channelHash"], dye_hash=payload["dyeHash"]
509        )

Deserialize a JSON payload of a character's dye information.

Parameters
Returns
  • aiobungie.crates.character.Dye: Information about a character dye object.
def deserialize_character_customization( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.character.CustomizationOptions:
511    def deserialize_character_customization(
512        self, payload: typedefs.JSONObject
513    ) -> character.CustomizationOptions:
514        return character.CustomizationOptions(
515            personality=payload["personality"],
516            face=payload["face"],
517            skin_color=payload["skinColor"],
518            lip_color=payload["lipColor"],
519            eye_color=payload["eyeColor"],
520            hair_colors=payload.get("hairColors", []),
521            feature_colors=payload.get("featureColors", []),
522            decal_color=payload["decalColor"],
523            wear_helmet=payload["wearHelmet"],
524            hair_index=payload["hairIndex"],
525            feature_index=payload["featureIndex"],
526            decal_index=payload["decalIndex"],
527        )

Deserialize a JSON payload of a character customization information found in character render data profile component.

Parameters
Returns
  • aiobungie.crates.character.CustomizationOptions: Information about a character customs object.
def deserialize_character_minimal_equipments( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.character.MinimalEquipments:
529    def deserialize_character_minimal_equipments(
530        self, payload: typedefs.JSONObject
531    ) -> character.MinimalEquipments:
532        if raw_dyes := payload.get("dyes"):
533            dyes = [self.deserialize_character_dye(dye) for dye in raw_dyes]
534        else:
535            dyes = []
536
537        return character.MinimalEquipments(
538            net=self._net, item_hash=payload["itemHash"], dyes=dyes
539        )

Deserialize a singular JSON peer view of equipment found in character render data profile component.

Parameters
Returns
  • aiobungie.crates.character.MinimalEquipments: A minimal equipment object.
def deserialize_character_render_data( self, payload: collections.abc.Mapping[str, typing.Any], /) -> aiobungie.crates.character.RenderedData:
541    def deserialize_character_render_data(
542        self, payload: typedefs.JSONObject, /
543    ) -> character.RenderedData:
544        return character.RenderedData(
545            net=self._net,
546            customization=self.deserialize_character_customization(
547                payload["customization"]
548            ),
549            custom_dyes=[
550                self.deserialize_character_dye(dye)
551                for dye in payload["customDyes"]
552                if dye
553            ],
554            equipment=[
555                self.deserialize_character_minimal_equipments(equipment)
556                for equipment in payload["peerView"]["equipment"]
557            ],
558        )

Deserialize a JSON payload of a profile character render data component.

Parameters
Returns
def deserialize_available_activity( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.activity.AvailableActivity:
560    def deserialize_available_activity(
561        self, payload: typedefs.JSONObject
562    ) -> activity.AvailableActivity:
563        return activity.AvailableActivity(
564            hash=payload["activityHash"],
565            is_new=payload["isNew"],
566            is_completed=payload["isCompleted"],
567            is_visible=payload["isVisible"],
568            display_level=payload.get("displayLevel"),
569            recommended_light=payload.get("recommendedLight"),
570            difficulty=activity.Difficulty(payload["difficultyTier"]),
571            can_join=payload["canJoin"],
572            can_lead=payload["canLead"],
573        )

Deserialize a JSON payload of an available activities.

This method is used to deserialize an array of aiobungie.crates.CharacterActivity.available_activities.

Parameters
Returns
def deserialize_character_activity( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.activity.CharacterActivity:
575    def deserialize_character_activity(
576        self, payload: typedefs.JSONObject
577    ) -> activity.CharacterActivity:
578        current_mode: enums.GameMode | None = None
579        if raw_current_mode := payload.get("currentActivityModeType"):
580            current_mode = enums.GameMode(raw_current_mode)
581
582        if raw_current_modes := payload.get("currentActivityModeTypes"):
583            current_mode_types = [enums.GameMode(type_) for type_ in raw_current_modes]
584        else:
585            current_mode_types = []
586
587        return activity.CharacterActivity(
588            date_started=time.clean_date(payload["dateActivityStarted"]),
589            current_hash=payload["currentActivityHash"],
590            current_mode_hash=payload["currentActivityModeHash"],
591            current_mode=current_mode,
592            current_mode_hashes=payload.get("currentActivityModeHashes", []),
593            current_mode_types=current_mode_types,
594            current_playlist_hash=payload.get("currentPlaylistActivityHash"),
595            last_story_hash=payload["lastCompletedStoryHash"],
596            available_activities=[
597                self.deserialize_available_activity(activity_)
598                for activity_ in payload["availableActivities"]
599            ],
600        )

Deserialize a JSON payload of character activity profile component.

Parameters
Returns
def deserialize_profile_items( self, payload: collections.abc.Mapping[str, typing.Any], /) -> list[aiobungie.crates.profile.ProfileItemImpl]:
602    def deserialize_profile_items(
603        self, payload: typedefs.JSONObject, /
604    ) -> list[profile.ProfileItemImpl]:
605        return [self.deserialize_profile_item(item) for item in payload["items"]]

Deserialize a JSON payload of profile items component information.

This may deserialize profileInventories or profileCurrencies or any other alternatives.

Parameters
Returns
def deserialize_progressions( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.progressions.Progression:
648    def deserialize_progressions(
649        self, payload: typedefs.JSONObject
650    ) -> progressions.Progression:
651        return progressions.Progression(
652            hash=int(payload["progressionHash"]),
653            level=int(payload["level"]),
654            cap=int(payload["levelCap"]),
655            daily_limit=int(payload["dailyLimit"]),
656            weekly_limit=int(payload["weeklyLimit"]),
657            current_progress=int(payload["currentProgress"]),
658            daily_progress=int(payload["dailyProgress"]),
659            needed=int(payload["progressToNextLevel"]),
660            next_level=int(payload["nextLevelAt"]),
661        )
def deserialize_milestone( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.milestones.Milestone:
746    def deserialize_milestone(
747        self, payload: typedefs.JSONObject
748    ) -> milestones.Milestone:
749        start_date: datetime.datetime | None = None
750        if raw_start_date := payload.get("startDate"):
751            start_date = time.clean_date(raw_start_date)
752
753        end_date: datetime.datetime | None = None
754        if raw_end_date := payload.get("endDate"):
755            end_date = time.clean_date(raw_end_date)
756
757        rewards: collections.Collection[milestones.MilestoneReward] | None = None
758        if raw_rewards := payload.get("rewards"):
759            rewards = [
760                self._deserialize_milestone_rewards(reward) for reward in raw_rewards
761            ]
762
763        activities: collections.Sequence[milestones.MilestoneActivity] | None = None
764        if raw_activities := payload.get("activities"):
765            activities = [
766                self._deserialize_milestone_activity(active)
767                for active in raw_activities
768            ]
769
770        quests: collections.Sequence[milestones.MilestoneQuest] | None = None
771        if raw_quests := payload.get("availableQuests"):
772            quests = [
773                self._deserialize_milestone_available_quest(quest)
774                for quest in raw_quests
775            ]
776
777        vendors: collections.Sequence[milestones.MilestoneVendor] | None = None
778        if raw_vendors := payload.get("vendors"):
779            vendors = [
780                milestones.MilestoneVendor(
781                    vendor_hash=vendor["vendorHash"],
782                    preview_itemhash=vendor.get("previewItemHash"),
783                )
784                for vendor in raw_vendors
785            ]
786
787        return milestones.Milestone(
788            hash=payload["milestoneHash"],
789            start_date=start_date,
790            end_date=end_date,
791            order=payload["order"],
792            rewards=rewards,
793            available_quests=quests,
794            activities=activities,
795            vendors=vendors,
796        )
def deserialize_characters( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.Character]:
813    def deserialize_characters(
814        self, payload: typedefs.JSONObject
815    ) -> collections.Mapping[int, character.Character]:
816        return {
817            int(char_id): self._set_character_attrs(char)
818            for char_id, char in payload["data"].items()
819        }
def deserialize_character( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.character.Character:
821    def deserialize_character(
822        self, payload: typedefs.JSONObject
823    ) -> character.Character:
824        return self._set_character_attrs(payload)
def deserialize_character_equipments( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Mapping[int, collections.abc.Sequence[aiobungie.crates.profile.ProfileItemImpl]]:
826    def deserialize_character_equipments(
827        self, payload: typedefs.JSONObject
828    ) -> collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]:
829        return {
830            int(char_id): self.deserialize_profile_items(item)
831            for char_id, item in payload["data"].items()
832        }
def deserialize_character_activities( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.activity.CharacterActivity]:
834    def deserialize_character_activities(
835        self, payload: typedefs.JSONObject
836    ) -> collections.Mapping[int, activity.CharacterActivity]:
837        return {
838            int(char_id): self.deserialize_character_activity(data)
839            for char_id, data in payload["data"].items()
840        }
def deserialize_characters_render_data( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.RenderedData]:
842    def deserialize_characters_render_data(
843        self, payload: typedefs.JSONObject
844    ) -> collections.Mapping[int, character.RenderedData]:
845        return {
846            int(char_id): self.deserialize_character_render_data(data)
847            for char_id, data in payload["data"].items()
848        }
def deserialize_character_progressions( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.character.CharacterProgression:
850    def deserialize_character_progressions(
851        self, payload: typedefs.JSONObject
852    ) -> character.CharacterProgression:
853        progressions_ = {
854            int(prog_id): self.deserialize_progressions(prog)
855            for prog_id, prog in payload["progressions"].items()
856        }
857
858        factions = {
859            int(faction_id): self._deserialize_factions(faction)
860            for faction_id, faction in payload["factions"].items()
861        }
862
863        milestones_ = {
864            int(milestone_hash): self.deserialize_milestone(milestone)
865            for milestone_hash, milestone in payload["milestones"].items()
866        }
867
868        uninstanced_item_objectives = {
869            int(item_hash): [self.deserialize_objectives(ins) for ins in obj]
870            for item_hash, obj in payload["uninstancedItemObjectives"].items()
871        }
872
873        artifact = payload["seasonalArtifact"]
874        seasonal_artifact = season.CharacterScopedArtifact(
875            hash=artifact["artifactHash"],
876            points_used=artifact["pointsUsed"],
877            reset_count=artifact["resetCount"],
878            tiers=[
879                self._deserialize_artifact_tiers(tier) for tier in artifact["tiers"]
880            ],
881        )
882        checklists = payload["checklists"]
883
884        return character.CharacterProgression(
885            progressions=progressions_,
886            factions=factions,
887            checklists=checklists,
888            milestones=milestones_,
889            seasonal_artifact=seasonal_artifact,
890            uninstanced_item_objectives=uninstanced_item_objectives,
891        )
def deserialize_character_progressions_mapping( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.CharacterProgression]:
893    def deserialize_character_progressions_mapping(
894        self, payload: typedefs.JSONObject
895    ) -> collections.Mapping[int, character.CharacterProgression]:
896        character_progressions: collections.Mapping[
897            int, character.CharacterProgression
898        ] = {}
899        for char_id, data in payload["data"].items():
900            # A little hack to stop mypy complaining about Mapping <-> dict
901            character_progressions[
902                int(char_id)
903            ] = self.deserialize_character_progressions(data)  # type: ignore[index]
904        return character_progressions
def deserialize_characters_records( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.records.CharacterRecord]:
906    def deserialize_characters_records(
907        self,
908        payload: typedefs.JSONObject,
909    ) -> collections.Mapping[int, records.CharacterRecord]:
910        return {
911            int(rec_id): self.deserialize_character_records(
912                rec, record_hashes=payload.get("featuredRecordHashes")
913            )
914            for rec_id, rec in payload["records"].items()
915        }
def deserialize_profile_records( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.records.Record]:
917    def deserialize_profile_records(
918        self, payload: typedefs.JSONObject
919    ) -> collections.Mapping[int, records.Record]:
920        raw_profile_records = payload["data"]
921        scores = records.RecordScores(
922            current_score=raw_profile_records["score"],
923            legacy_score=raw_profile_records["legacyScore"],
924            lifetime_score=raw_profile_records["lifetimeScore"],
925        )
926        return {
927            int(record_id): self.deserialize_records(
928                record,
929                scores,
930                categories_hash=raw_profile_records["recordCategoriesRootNodeHash"],
931                seals_hash=raw_profile_records["recordSealsRootNodeHash"],
932            )
933            for record_id, record in raw_profile_records["records"].items()
934        }
def deserialize_craftables_component( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.components.CraftablesComponent:
969    def deserialize_craftables_component(
970        self, payload: typedefs.JSONObject
971    ) -> components.CraftablesComponent:
972        return components.CraftablesComponent(
973            net=self._net,
974            craftables={
975                int(item_id): self._deserialize_craftable_item(item)
976                for item_id, item in payload["craftables"].items()
977                if item is not None
978            },
979            crafting_root_node_hash=payload["craftingRootNodeHash"],
980        )
def deserialize_components( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.components.Component:
 982    def deserialize_components(  # noqa: C901 Too complex.
 983        self, payload: typedefs.JSONObject
 984    ) -> components.Component:
 985        # Due to how complex this method is, We'll stick to
 986        # typing.Optional here.
 987
 988        profile_: profile.Profile | None = None
 989        if raw_profile := payload.get("profile"):
 990            profile_ = self.deserialize_profile(raw_profile)
 991
 992        profile_progression: profile.ProfileProgression | None = None
 993        if raw_profile_progression := payload.get("profileProgression"):
 994            profile_progression = self.deserialize_profile_progression(
 995                raw_profile_progression
 996            )
 997
 998        profile_currencies: typing.Optional[
 999            collections.Sequence[profile.ProfileItemImpl]
1000        ] = None
1001        if raw_profile_currencies := payload.get("profileCurrencies"):
1002            if "data" in raw_profile_currencies:
1003                profile_currencies = self.deserialize_profile_items(
1004                    raw_profile_currencies["data"]
1005                )
1006
1007        profile_inventories: typing.Optional[
1008            collections.Sequence[profile.ProfileItemImpl]
1009        ] = None
1010        if raw_profile_inventories := payload.get("profileInventory"):
1011            if "data" in raw_profile_inventories:
1012                profile_inventories = self.deserialize_profile_items(
1013                    raw_profile_inventories["data"]
1014                )
1015
1016        profile_records: typing.Optional[
1017            collections.Mapping[int, records.Record]
1018        ] = None
1019
1020        if raw_profile_records_ := payload.get("profileRecords"):
1021            profile_records = self.deserialize_profile_records(raw_profile_records_)
1022
1023        characters: typing.Optional[
1024            collections.Mapping[int, character.Character]
1025        ] = None
1026        if raw_characters := payload.get("characters"):
1027            characters = self.deserialize_characters(raw_characters)
1028
1029        character_records: typing.Optional[
1030            collections.Mapping[int, records.CharacterRecord]
1031        ] = None
1032
1033        if raw_character_records := payload.get("characterRecords"):
1034            # Had to do it in two steps..
1035            to_update = {}
1036            for _, data in raw_character_records["data"].items():
1037                for record_id, record in data.items():
1038                    to_update[record_id] = record
1039
1040            character_records = {
1041                int(rec_id): self.deserialize_character_records(
1042                    rec, record_hashes=to_update.get("featuredRecordHashes")
1043                )
1044                for rec_id, rec in to_update["records"].items()
1045            }
1046
1047        character_equipments: typing.Optional[
1048            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1049        ] = None
1050        if raw_character_equips := payload.get("characterEquipment"):
1051            character_equipments = self.deserialize_character_equipments(
1052                raw_character_equips
1053            )
1054
1055        character_inventories: typing.Optional[
1056            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1057        ] = None
1058        if raw_character_inventories := payload.get("characterInventories"):
1059            if "data" in raw_character_inventories:
1060                character_inventories = self.deserialize_character_equipments(
1061                    raw_character_inventories
1062                )
1063
1064        character_activities: typing.Optional[
1065            collections.Mapping[int, activity.CharacterActivity]
1066        ] = None
1067        if raw_char_acts := payload.get("characterActivities"):
1068            character_activities = self.deserialize_character_activities(raw_char_acts)
1069
1070        character_render_data: typing.Optional[
1071            collections.Mapping[int, character.RenderedData]
1072        ] = None
1073        if raw_character_render_data := payload.get("characterRenderData"):
1074            character_render_data = self.deserialize_characters_render_data(
1075                raw_character_render_data
1076            )
1077
1078        character_progressions: typing.Optional[
1079            collections.Mapping[int, character.CharacterProgression]
1080        ] = None
1081
1082        if raw_character_progressions := payload.get("characterProgressions"):
1083            character_progressions = self.deserialize_character_progressions_mapping(
1084                raw_character_progressions
1085            )
1086
1087        profile_string_vars: typing.Optional[collections.Mapping[int, int]] = None
1088        if raw_profile_string_vars := payload.get("profileStringVariables"):
1089            profile_string_vars = raw_profile_string_vars["data"]["integerValuesByHash"]
1090
1091        character_string_vars: typing.Optional[
1092            collections.Mapping[int, collections.Mapping[int, int]]
1093        ] = None
1094        if raw_character_string_vars := payload.get("characterStringVariables"):
1095            character_string_vars = {
1096                int(char_id): data["integerValuesByHash"]
1097                for char_id, data in raw_character_string_vars["data"].items()
1098            }
1099
1100        metrics: typing.Optional[
1101            collections.Sequence[
1102                collections.Mapping[int, tuple[bool, records.Objective | None]]
1103            ]
1104        ] = None
1105        root_node_hash: int | None = None
1106
1107        if raw_metrics := payload.get("metrics"):
1108            root_node_hash = raw_metrics["data"]["metricsRootNodeHash"]
1109            metrics = [
1110                {
1111                    int(metrics_hash): (
1112                        data["invisible"],
1113                        self.deserialize_objectives(data["objectiveProgress"])
1114                        if "objectiveProgress" in data
1115                        else None,
1116                    )
1117                    for metrics_hash, data in raw_metrics["data"]["metrics"].items()
1118                }
1119            ]
1120        transitory: fireteams.FireteamParty | None = None
1121        if raw_transitory := payload.get("profileTransitoryData"):
1122            if "data" in raw_transitory:
1123                transitory = self.deserialize_fireteam_party(raw_transitory["data"])
1124
1125        item_components: components.ItemsComponent | None = None
1126        if raw_item_components := payload.get("itemComponents"):
1127            item_components = self.deserialize_items_component(raw_item_components)
1128
1129        profile_plugsets: typing.Optional[
1130            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1131        ] = None
1132
1133        if raw_profile_plugs := payload.get("profilePlugSets"):
1134            profile_plugsets = {
1135                int(index): [self.deserialize_plug_item_state(state) for state in data]
1136                for index, data in raw_profile_plugs["data"]["plugs"].items()
1137            }
1138
1139        character_plugsets: typing.Optional[
1140            collections.Mapping[
1141                int, collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1142            ]
1143        ] = None
1144        if raw_char_plugsets := payload.get("characterPlugSets"):
1145            character_plugsets = {
1146                int(char_id): {
1147                    int(index): [
1148                        self.deserialize_plug_item_state(state) for state in data
1149                    ]
1150                    for index, data in inner["plugs"].items()
1151                }
1152                for char_id, inner in raw_char_plugsets["data"].items()
1153            }
1154
1155        character_collectibles: typing.Optional[
1156            collections.Mapping[int, items.Collectible]
1157        ] = None
1158        if raw_character_collectibles := payload.get("characterCollectibles"):
1159            character_collectibles = {
1160                int(char_id): self._deserialize_collectible(data)
1161                for char_id, data in raw_character_collectibles["data"].items()
1162            }
1163
1164        profile_collectibles: items.Collectible | None = None
1165        if raw_profile_collectibles := payload.get("profileCollectibles"):
1166            profile_collectibles = self._deserialize_collectible(
1167                raw_profile_collectibles["data"]
1168            )
1169
1170        profile_nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1171        if raw_profile_nodes := payload.get("profilePresentationNodes"):
1172            profile_nodes = {
1173                int(node_hash): self._deserialize_node(node)
1174                for node_hash, node in raw_profile_nodes["data"]["nodes"].items()
1175            }
1176
1177        character_nodes: typing.Optional[
1178            collections.Mapping[int, collections.Mapping[int, records.Node]]
1179        ] = None
1180        if raw_character_nodes := payload.get("characterPresentationNodes"):
1181            character_nodes = {
1182                int(char_id): {
1183                    int(node_hash): self._deserialize_node(node)
1184                    for node_hash, node in each_character["nodes"].items()
1185                }
1186                for char_id, each_character in raw_character_nodes["data"].items()
1187            }
1188
1189        platform_silver: typing.Optional[
1190            collections.Mapping[str, profile.ProfileItemImpl]
1191        ] = None
1192        if raw_platform_silver := payload.get("platformSilver"):
1193            if "data" in raw_platform_silver:
1194                platform_silver = {
1195                    platform_name: self.deserialize_profile_item(item)
1196                    for platform_name, item in raw_platform_silver["data"][
1197                        "platformSilver"
1198                    ].items()
1199                }
1200
1201        character_currency_lookups: typing.Optional[
1202            collections.Mapping[int, collections.Sequence[items.Currency]]
1203        ] = None
1204        if raw_char_lookups := payload.get("characterCurrencyLookups"):
1205            if "data" in raw_char_lookups:
1206                character_currency_lookups = {
1207                    int(char_id): self._deserialize_currencies(currency)
1208                    for char_id, currency in raw_char_lookups["data"].items()
1209                }
1210
1211        character_craftables: typing.Optional[
1212            collections.Mapping[int, components.CraftablesComponent]
1213        ] = None
1214        if raw_character_craftables := payload.get("characterCraftables"):
1215            if "data" in raw_character_craftables:
1216                character_craftables = {
1217                    int(char_id): self.deserialize_craftables_component(craftable)
1218                    for char_id, craftable in raw_character_craftables["data"].items()
1219                }
1220
1221        return components.Component(
1222            profiles=profile_,
1223            profile_progression=profile_progression,
1224            profile_currencies=profile_currencies,
1225            profile_inventories=profile_inventories,
1226            profile_records=profile_records,
1227            characters=characters,
1228            character_records=character_records,
1229            character_equipments=character_equipments,
1230            character_inventories=character_inventories,
1231            character_activities=character_activities,
1232            character_render_data=character_render_data,
1233            character_progressions=character_progressions,
1234            profile_string_variables=profile_string_vars,
1235            character_string_variables=character_string_vars,
1236            metrics=metrics,
1237            root_node_hash=root_node_hash,
1238            transitory=transitory,
1239            item_components=item_components,
1240            profile_plugsets=profile_plugsets,
1241            character_plugsets=character_plugsets,
1242            character_collectibles=character_collectibles,
1243            profile_collectibles=profile_collectibles,
1244            profile_nodes=profile_nodes,
1245            character_nodes=character_nodes,
1246            platform_silver=platform_silver,
1247            character_currency_lookups=character_currency_lookups,
1248            character_craftables=character_craftables,
1249        )

Deserialize a JSON payload of Bungie.net profile components information.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_items_component( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.components.ItemsComponent:
1251    def deserialize_items_component(
1252        self, payload: typedefs.JSONObject
1253    ) -> components.ItemsComponent:
1254        # Due to how complex this method is, We'll stick to typing.Optional.
1255        instances: typing.Optional[
1256            collections.Sequence[collections.Mapping[int, items.ItemInstance]]
1257        ] = None
1258        if raw_instances := payload.get("instances"):
1259            instances = [
1260                {
1261                    int(ins_id): self.deserialize_instanced_item(item)
1262                    for ins_id, item in raw_instances["data"].items()
1263                }
1264            ]
1265
1266        render_data: typing.Optional[
1267            collections.Mapping[int, tuple[bool, dict[int, int]]]
1268        ] = None
1269        if raw_render_data := payload.get("renderData"):
1270            render_data = {
1271                int(ins_id): (data["useCustomDyes"], data["artRegions"])
1272                for ins_id, data in raw_render_data["data"].items()
1273            }
1274
1275        stats: typing.Optional[collections.Mapping[int, items.ItemStatsView]] = None
1276        if raw_stats := payload.get("stats"):
1277            builder: collections.Mapping[int, items.ItemStatsView] = {}
1278            for ins_id, stat in raw_stats["data"].items():
1279                for _, items_ in stat.items():
1280                    builder[int(ins_id)] = self.deserialize_item_stats_view(items_)  # type: ignore[index]
1281            stats = builder
1282
1283        sockets: typing.Optional[
1284            collections.Mapping[int, collections.Sequence[items.ItemSocket]]
1285        ] = None
1286        if raw_sockets := payload.get("sockets"):
1287            sockets = {
1288                int(ins_id): [
1289                    self.deserialize_item_socket(socket) for socket in item["sockets"]
1290                ]
1291                for ins_id, item in raw_sockets["data"].items()
1292            }
1293
1294        objectives: typing.Optional[
1295            collections.Mapping[int, collections.Sequence[records.Objective]]
1296        ] = None
1297        if raw_objectives := payload.get("objectives"):
1298            objectives = {
1299                int(ins_id): [self.deserialize_objectives(objective)]
1300                for ins_id, data in raw_objectives["data"].items()
1301                for objective in data["objectives"]
1302            }
1303
1304        perks: typing.Optional[
1305            collections.Mapping[int, collections.Collection[items.ItemPerk]]
1306        ] = None
1307        if raw_perks := payload.get("perks"):
1308            perks = {
1309                int(ins_id): [
1310                    self.deserialize_item_perk(perk) for perk in item["perks"]
1311                ]
1312                for ins_id, item in raw_perks["data"].items()
1313            }
1314
1315        plug_states: typing.Optional[collections.Sequence[items.PlugItemState]] = None
1316        if raw_plug_states := payload.get("plugStates"):
1317            pending_states: list[items.PlugItemState] = []
1318            for _, plug in raw_plug_states["data"].items():
1319                pending_states.append(self.deserialize_plug_item_state(plug))
1320            plug_states = pending_states
1321
1322        reusable_plugs: typing.Optional[
1323            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1324        ] = None
1325        if raw_re_plugs := payload.get("reusablePlugs"):
1326            reusable_plugs = {
1327                int(ins_id): [
1328                    self.deserialize_plug_item_state(state) for state in inner
1329                ]
1330                for ins_id, plug in raw_re_plugs["data"].items()
1331                for inner in list(plug["plugs"].values())
1332            }
1333
1334        plug_objectives: typing.Optional[
1335            collections.Mapping[
1336                int, collections.Mapping[int, collections.Collection[records.Objective]]
1337            ]
1338        ] = None
1339        if raw_plug_objectives := payload.get("plugObjectives"):
1340            plug_objectives = {
1341                int(ins_id): {
1342                    int(obj_hash): [self.deserialize_objectives(obj) for obj in objs]
1343                    for obj_hash, objs in inner["objectivesPerPlug"].items()
1344                }
1345                for ins_id, inner in raw_plug_objectives["data"].items()
1346            }
1347
1348        return components.ItemsComponent(
1349            sockets=sockets,
1350            stats=stats,
1351            render_data=render_data,
1352            instances=instances,
1353            objectives=objectives,
1354            perks=perks,
1355            plug_states=plug_states,
1356            reusable_plugs=reusable_plugs,
1357            plug_objectives=plug_objectives,
1358        )

Deserialize a JSON objects within the itemComponents key.`

def deserialize_character_component( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.components.CharacterComponent:
1360    def deserialize_character_component(  # type: ignore[call-arg]
1361        self, payload: typedefs.JSONObject
1362    ) -> components.CharacterComponent:
1363        character_: character.Character | None = None
1364        if raw_singular_character := payload.get("character"):
1365            character_ = self.deserialize_character(raw_singular_character["data"])
1366
1367        inventory: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1368        if raw_inventory := payload.get("inventory"):
1369            if "data" in raw_inventory:
1370                inventory = self.deserialize_profile_items(raw_inventory["data"])
1371
1372        activities: activity.CharacterActivity | None = None
1373        if raw_activities := payload.get("activities"):
1374            activities = self.deserialize_character_activity(raw_activities["data"])
1375
1376        equipment: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1377        if raw_equipments := payload.get("equipment"):
1378            equipment = self.deserialize_profile_items(raw_equipments["data"])
1379
1380        progressions_: character.CharacterProgression | None = None
1381        if raw_progressions := payload.get("progressions"):
1382            progressions_ = self.deserialize_character_progressions(
1383                raw_progressions["data"]
1384            )
1385
1386        render_data: character.RenderedData | None = None
1387        if raw_render_data := payload.get("renderData"):
1388            render_data = self.deserialize_character_render_data(
1389                raw_render_data["data"]
1390            )
1391
1392        character_records: typing.Optional[
1393            collections.Mapping[int, records.CharacterRecord]
1394        ] = None
1395        if raw_char_records := payload.get("records"):
1396            character_records = self.deserialize_characters_records(
1397                raw_char_records["data"]
1398            )
1399
1400        item_components: components.ItemsComponent | None = None
1401        if raw_item_components := payload.get("itemComponents"):
1402            item_components = self.deserialize_items_component(raw_item_components)
1403
1404        nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1405        if raw_nodes := payload.get("presentationNodes"):
1406            nodes = {
1407                int(node_hash): self._deserialize_node(node)
1408                for node_hash, node in raw_nodes["data"]["nodes"].items()
1409            }
1410
1411        collectibles: items.Collectible | None = None
1412        if raw_collectibles := payload.get("collectibles"):
1413            collectibles = self._deserialize_collectible(raw_collectibles["data"])
1414
1415        currency_lookups: typing.Optional[collections.Sequence[items.Currency]] = None
1416        if raw_currencies := payload.get("currencyLookups"):
1417            if "data" in raw_currencies:
1418                currency_lookups = self._deserialize_currencies(raw_currencies)
1419
1420        return components.CharacterComponent(
1421            activities=activities,
1422            equipment=equipment,
1423            inventory=inventory,
1424            progressions=progressions_,
1425            render_data=render_data,
1426            character=character_,
1427            character_records=character_records,
1428            profile_records=None,
1429            item_components=item_components,
1430            currency_lookups=currency_lookups,
1431            collectibles=collectibles,
1432            nodes=nodes,
1433        )

Deserialize a JSON payload of Destiny 2 character component.

Parameters
Returns
def deserialize_inventory_results( self, payload: collections.abc.Mapping[str, typing.Any]) -> Iterator[aiobungie.crates.entity.SearchableEntity]:
1452    def deserialize_inventory_results(
1453        self, payload: typedefs.JSONObject
1454    ) -> iterators.Iterator[entity.SearchableEntity]:
1455        return iterators.Iterator(
1456            [
1457                entity.SearchableEntity(
1458                    net=self._net,
1459                    hash=data["hash"],
1460                    entity_type=data["entityType"],
1461                    weight=data["weight"],
1462                    suggested_words=payload["suggestedWords"],
1463                    name=data["displayProperties"]["name"],
1464                    has_icon=data["displayProperties"]["hasIcon"],
1465                    description=typedefs.unknown(
1466                        data["displayProperties"]["description"]
1467                    ),
1468                    icon=assets.Image(data["displayProperties"]["icon"]),
1469                )
1470                for data in payload["results"]["results"]
1471            ]
1472        )

Deserialize results of searched Destiny2 entities.

Parameters
Returns
def deserialize_inventory_entity( self, payload: collections.abc.Mapping[str, typing.Any], /) -> aiobungie.crates.entity.InventoryEntity:
1501    def deserialize_inventory_entity(  # noqa: C901 Too complex.
1502        self, payload: typedefs.JSONObject, /
1503    ) -> entity.InventoryEntity:
1504        props = self._set_entity_attrs(payload)
1505        objects = self._deserialize_inventory_item_objects(payload)
1506
1507        collectible_hash: int | None = None
1508        if raw_collectible_hash := payload.get("collectibleHash"):
1509            collectible_hash = int(raw_collectible_hash)
1510
1511        secondary_icon: assets.Image | None = None
1512        if raw_second_icon := payload.get("secondaryIcon"):
1513            secondary_icon = assets.Image(raw_second_icon)
1514
1515        secondary_overlay: assets.Image | None = None
1516        if raw_second_overlay := payload.get("secondaryOverlay"):
1517            secondary_overlay = assets.Image(raw_second_overlay)
1518
1519        secondary_special: assets.Image | None = None
1520        if raw_second_special := payload.get("secondarySpecial"):
1521            secondary_special = assets.Image(raw_second_special)
1522
1523        screenshot: assets.Image | None = None
1524        if raw_screenshot := payload.get("screenshot"):
1525            screenshot = assets.Image(raw_screenshot)
1526
1527        watermark_icon: assets.Image | None = None
1528        if raw_watermark_icon := payload.get("iconWatermark"):
1529            watermark_icon = assets.Image(raw_watermark_icon)
1530
1531        watermark_shelved: assets.Image | None = None
1532        if raw_watermark_shelved := payload.get("iconWatermarkShelved"):
1533            watermark_shelved = assets.Image(raw_watermark_shelved)
1534
1535        about: str | None = None
1536        if raw_about := payload.get("flavorText"):
1537            about = raw_about
1538
1539        ui_item_style: str | None = None
1540        if raw_ui_style := payload.get("uiItemDisplayStyle"):
1541            ui_item_style = raw_ui_style
1542
1543        tier_and_name: str | None = None
1544        if raw_tier_and_name := payload.get("itemTypeAndTierDisplayName"):
1545            tier_and_name = raw_tier_and_name
1546
1547        type_name: str | None = None
1548        if raw_type_name := payload.get("itemTypeDisplayName"):
1549            type_name = raw_type_name
1550
1551        display_source: str | None = None
1552        if raw_display_source := payload.get("displaySource"):
1553            display_source = raw_display_source
1554
1555        lorehash: int | None = None
1556        if raw_lore_hash := payload.get("loreHash"):
1557            lorehash = int(raw_lore_hash)
1558
1559        summary_hash: int | None = None
1560        if raw_summary_hash := payload.get("summaryItemHash"):
1561            summary_hash = raw_summary_hash
1562
1563        breaker_type_hash: int | None = None
1564        if raw_breaker_type_hash := payload.get("breakerTypeHash"):
1565            breaker_type_hash = int(raw_breaker_type_hash)
1566
1567        damage_types: typing.Optional[collections.Sequence[int]] = None
1568        if raw_damage_types := payload.get("damageTypes"):
1569            damage_types = [int(type_) for type_ in raw_damage_types]
1570
1571        damagetype_hashes: typing.Optional[collections.Sequence[int]] = None
1572        if raw_damagetype_hashes := payload.get("damageTypeHashes"):
1573            damagetype_hashes = [int(type_) for type_ in raw_damagetype_hashes]
1574
1575        default_damagetype_hash: int | None = None
1576        if raw_defaultdmg_hash := payload.get("defaultDamageTypeHash"):
1577            default_damagetype_hash = int(raw_defaultdmg_hash)
1578
1579        emblem_objective_hash: int | None = None
1580        if raw_emblem_obj_hash := payload.get("emblemObjectiveHash"):
1581            emblem_objective_hash = int(raw_emblem_obj_hash)
1582
1583        tier_type: enums.TierType | None = None
1584        tier: enums.ItemTier | None = None
1585        bucket_hash: int | None = None
1586        recovery_hash: int | None = None
1587        tier_name: str | None = None
1588        isinstance_item: bool = False
1589        expire_tool_tip: str | None = None
1590        expire_in_orbit_message: str | None = None
1591        suppress_expiration: bool = False
1592        max_stack_size: int | None = None
1593        stack_label: str | None = None
1594
1595        if inventory := payload.get("inventory"):
1596            tier_type = enums.TierType(int(inventory["tierType"]))
1597            tier = enums.ItemTier(int(inventory["tierTypeHash"]))
1598            bucket_hash = int(inventory["bucketTypeHash"])
1599            recovery_hash = int(inventory["recoveryBucketTypeHash"])
1600            tier_name = inventory["tierTypeName"]
1601            isinstance_item = inventory["isInstanceItem"]
1602            suppress_expiration = inventory["suppressExpirationWhenObjectivesComplete"]
1603            max_stack_size = int(inventory["maxStackSize"])
1604
1605            try:
1606                stack_label = inventory["stackUniqueLabel"]
1607            except KeyError:
1608                pass
1609
1610        return entity.InventoryEntity(
1611            net=self._net,
1612            collectible_hash=collectible_hash,
1613            name=props.name,
1614            about=about,
1615            emblem_objective_hash=emblem_objective_hash,
1616            suppress_expiration=suppress_expiration,
1617            max_stack_size=max_stack_size,
1618            stack_label=stack_label,
1619            tier=tier,
1620            tier_type=tier_type,
1621            tier_name=tier_name,
1622            bucket_hash=bucket_hash,
1623            recovery_bucket_hash=recovery_hash,
1624            isinstance_item=isinstance_item,
1625            expire_in_orbit_message=expire_in_orbit_message,
1626            expiration_tooltip=expire_tool_tip,
1627            lore_hash=lorehash,
1628            type_and_tier_name=tier_and_name,
1629            summary_hash=summary_hash,
1630            ui_display_style=ui_item_style,
1631            type_name=type_name,
1632            breaker_type_hash=breaker_type_hash,
1633            description=props.description,
1634            display_source=display_source,
1635            hash=props.hash,
1636            damage_types=damage_types,
1637            index=props.index,
1638            icon=props.icon,
1639            has_icon=props.has_icon,
1640            screenshot=screenshot,
1641            watermark_icon=watermark_icon,
1642            watermark_shelved=watermark_shelved,
1643            secondary_icon=secondary_icon,
1644            secondary_overlay=secondary_overlay,
1645            secondary_special=secondary_special,
1646            type=enums.ItemType(int(payload["itemType"])),
1647            trait_hashes=[int(id_) for id_ in payload.get("traitHashes", [])],
1648            trait_ids=[trait for trait in payload.get("traitIds", [])],
1649            category_hashes=[int(hash_) for hash_ in payload["itemCategoryHashes"]],
1650            item_class=enums.Class(int(payload["classType"])),
1651            sub_type=enums.ItemSubType(int(payload["itemSubType"])),
1652            breaker_type=int(payload["breakerType"]),
1653            default_damagetype=int(payload["defaultDamageType"]),
1654            default_damagetype_hash=default_damagetype_hash,
1655            damagetype_hashes=damagetype_hashes,
1656            tooltip_notifications=payload["tooltipNotifications"],
1657            not_transferable=payload["nonTransferrable"],
1658            allow_actions=payload["allowActions"],
1659            is_equippable=payload["equippable"],
1660            objects=objects,
1661            background_colors=payload.get("backgroundColor", {}),
1662            season_hash=payload.get("seasonHash"),
1663            has_postmaster_effect=payload["doesPostmasterPullHaveSideEffects"],
1664        )

Deserialize a JSON payload of an inventory entity item information.

This can be any item from DestinyInventoryItemDefinition definition.

Parameters
Returns
def deserialize_objective_entity( self, payload: collections.abc.Mapping[str, typing.Any], /) -> aiobungie.crates.entity.ObjectiveEntity:
1666    def deserialize_objective_entity(
1667        self, payload: typedefs.JSONObject, /
1668    ) -> entity.ObjectiveEntity:
1669        props = self._set_entity_attrs(payload)
1670        return entity.ObjectiveEntity(
1671            net=self._net,
1672            hash=props.hash,
1673            index=props.index,
1674            description=props.description,
1675            name=props.name,
1676            has_icon=props.has_icon,
1677            icon=props.icon,
1678            unlock_value_hash=payload["unlockValueHash"],
1679            completion_value=payload["completionValue"],
1680            scope=entity.GatingScope(int(payload["scope"])),
1681            location_hash=payload["locationHash"],
1682            allowed_negative_value=payload["allowNegativeValue"],
1683            allowed_value_change=payload["allowValueChangeWhenCompleted"],
1684            counting_downward=payload["isCountingDownward"],
1685            value_style=entity.ValueUIStyle(int(payload["valueStyle"])),
1686            progress_description=payload["progressDescription"],
1687            perks=payload["perks"],
1688            stats=payload["stats"],
1689            minimum_visibility=payload["minimumVisibilityThreshold"],
1690            allow_over_completion=payload["allowOvercompletion"],
1691            show_value_style=payload["showValueOnComplete"],
1692            display_only_objective=payload["isDisplayOnlyObjective"],
1693            complete_value_style=entity.ValueUIStyle(
1694                int(payload["completedValueStyle"])
1695            ),
1696            progress_value_style=entity.ValueUIStyle(
1697                int(payload["inProgressValueStyle"])
1698            ),
1699            ui_label=payload["uiLabel"],
1700            ui_style=entity.ObjectiveUIStyle(int(payload["uiStyle"])),
1701        )

Deserialize a JSON payload of an objective entity information.

Parameters
Returns
def deserialize_activity( self, payload: collections.abc.Mapping[str, typing.Any], /) -> aiobungie.crates.activity.Activity:
1729    def deserialize_activity(
1730        self,
1731        payload: typedefs.JSONObject,
1732        /,
1733    ) -> activity.Activity:
1734        period = time.clean_date(payload["period"])
1735        details = payload["activityDetails"]
1736        ref_id = int(details["referenceId"])
1737        instance_id = int(details["instanceId"])
1738        mode = enums.GameMode(details["mode"])
1739        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1740        is_private = details["isPrivate"]
1741        membership_type = enums.MembershipType(int(details["membershipType"]))
1742
1743        # Since we're using the same fields for post activity method
1744        # this check is required since post activity doesn't values values
1745        values = self._deserialize_activity_values(payload["values"])
1746
1747        return activity.Activity(
1748            net=self._net,
1749            hash=ref_id,
1750            instance_id=instance_id,
1751            mode=mode,
1752            modes=modes,
1753            is_private=is_private,
1754            membership_type=membership_type,
1755            occurred_at=period,
1756            values=values,
1757        )

Deserialize a JSON payload of an activity history information.

Parameters
Returns
def deserialize_activities( self, payload: collections.abc.Mapping[str, typing.Any]) -> Iterator[aiobungie.crates.activity.Activity]:
1759    def deserialize_activities(
1760        self, payload: typedefs.JSONObject
1761    ) -> iterators.Iterator[activity.Activity]:
1762        return iterators.Iterator(
1763            [
1764                self.deserialize_activity(activity_)
1765                for activity_ in payload["activities"]
1766            ]
1767        )

Deserialize a JSON payload of an array of activity history information.

Parameters
Returns
def deserialize_extended_weapon_values( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.activity.ExtendedWeaponValues:
1769    def deserialize_extended_weapon_values(
1770        self, payload: typedefs.JSONObject
1771    ) -> activity.ExtendedWeaponValues:
1772        assists: int | None = None
1773        if raw_assists := payload["values"].get("uniqueWeaponAssists"):
1774            assists = raw_assists["basic"]["value"]
1775        assists_damage: int | None = None
1776
1777        if raw_assists_damage := payload["values"].get("uniqueWeaponAssistDamage"):
1778            assists_damage = raw_assists_damage["basic"]["value"]
1779
1780        return activity.ExtendedWeaponValues(
1781            reference_id=int(payload["referenceId"]),
1782            kills=payload["values"]["uniqueWeaponKills"]["basic"]["value"],
1783            precision_kills=payload["values"]["uniqueWeaponPrecisionKills"]["basic"][
1784                "value"
1785            ],
1786            assists=assists,
1787            assists_damage=assists_damage,
1788            precision_kills_percentage=(
1789                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"]["value"],
1790                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"][
1791                    "displayValue"
1792                ],
1793            ),
1794        )

Deserialize values of extended weapons JSON object.

Parameters
Returns
def deserialize_post_activity_player( self, payload: collections.abc.Mapping[str, typing.Any], /) -> aiobungie.crates.activity.PostActivityPlayer:
1815    def deserialize_post_activity_player(
1816        self, payload: typedefs.JSONObject, /
1817    ) -> activity.PostActivityPlayer:
1818        player = payload["player"]
1819
1820        class_hash: int | None = None
1821        if (class_hash := player.get("classHash")) is not None:
1822            class_hash = class_hash
1823
1824        race_hash: int | None = None
1825        if (race_hash := player.get("raceHash")) is not None:
1826            race_hash = race_hash
1827
1828        gender_hash: int | None = None
1829        if (gender_hash := player.get("genderHash")) is not None:
1830            gender_hash = gender_hash
1831
1832        character_class: str | None = None
1833        if character_class := player.get("characterClass"):
1834            character_class = character_class
1835
1836        character_level: int | None = None
1837        if (character_level := player.get("characterLevel")) is not None:
1838            character_level = character_level
1839
1840        return activity.PostActivityPlayer(
1841            standing=int(payload["standing"]),
1842            score=int(payload["score"]["basic"]["value"]),
1843            character_id=payload["characterId"],
1844            destiny_user=self.deserialize_destiny_membership(player["destinyUserInfo"]),
1845            character_class=character_class,
1846            character_level=character_level,
1847            race_hash=race_hash,
1848            gender_hash=gender_hash,
1849            class_hash=class_hash,
1850            light_level=int(player["lightLevel"]),
1851            emblem_hash=int(player["emblemHash"]),
1852            values=self._deserialize_activity_values(payload["values"]),
1853            extended_values=self._deserialize_extended_values(payload["extended"]),
1854        )

Deserialize a JSON payload of a post activity player information.

Parameters
Returns
def deserialize_post_activity( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.activity.PostActivity:
1866    def deserialize_post_activity(
1867        self, payload: typedefs.JSONObject
1868    ) -> activity.PostActivity:
1869        period = time.clean_date(payload["period"])
1870        details = payload["activityDetails"]
1871        ref_id = int(details["referenceId"])
1872        instance_id = int(details["instanceId"])
1873        mode = enums.GameMode(details["mode"])
1874        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1875        is_private = details["isPrivate"]
1876        membership_type = enums.MembershipType(int(details["membershipType"]))
1877        return activity.PostActivity(
1878            net=self._net,
1879            hash=ref_id,
1880            membership_type=membership_type,
1881            instance_id=instance_id,
1882            mode=mode,
1883            modes=modes,
1884            is_private=is_private,
1885            occurred_at=period,
1886            starting_phase=int(payload["startingPhaseIndex"]),
1887            players=[
1888                self.deserialize_post_activity_player(player)
1889                for player in payload["entries"]
1890            ],
1891            teams=[
1892                self._deserialize_post_activity_team(team) for team in payload["teams"]
1893            ],
1894        )

Deserialize a JSON payload of a post activity information.

Parameters
Returns
def deserialize_aggregated_activity( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.activity.AggregatedActivity:
1932    def deserialize_aggregated_activity(
1933        self, payload: typedefs.JSONObject
1934    ) -> activity.AggregatedActivity:
1935        return activity.AggregatedActivity(
1936            hash=int(payload["activityHash"]),
1937            values=self._deserialize_aggregated_activity_values(payload["values"]),
1938        )

Deserialize a JSON payload of an aggregated activity.

Parameters
Returns
def deserialize_aggregated_activities( self, payload: collections.abc.Mapping[str, typing.Any]) -> Iterator[aiobungie.crates.activity.AggregatedActivity]:
1940    def deserialize_aggregated_activities(
1941        self, payload: typedefs.JSONObject
1942    ) -> iterators.Iterator[activity.AggregatedActivity]:
1943        return iterators.Iterator(
1944            [
1945                self.deserialize_aggregated_activity(activity)
1946                for activity in payload["activities"]
1947            ]
1948        )

Deserialize a JSON payload of an array of aggregated activities.

Parameters
Returns
def deserialize_linked_profiles( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.profile.LinkedProfile:
1950    def deserialize_linked_profiles(
1951        self, payload: typedefs.JSONObject
1952    ) -> profile.LinkedProfile:
1953        bungie_user = self.deserialize_partial_bungie_user(payload["bnetMembership"])
1954        error_profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
1955        profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
1956
1957        if raw_profile := payload.get("profiles"):
1958            for pfile in raw_profile:
1959                profiles_vec.append(self.deserialize_destiny_membership(pfile))
1960
1961        if raw_profiles_with_errors := payload.get("profilesWithErrors"):
1962            for raw_error_pfile in raw_profiles_with_errors:
1963                if error_pfile := raw_error_pfile.get("infoCard"):
1964                    error_profiles_vec.append(
1965                        self.deserialize_destiny_membership(error_pfile)
1966                    )
1967
1968        return profile.LinkedProfile(
1969            bungie_user=bungie_user,
1970            profiles=profiles_vec,
1971            profiles_with_errors=error_profiles_vec,
1972        )

Deserialize a JSON payload of Bungie.net hard linked profile information.

Parameters
Returns
def deserialize_clan_banners( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.clans.ClanBanner]:
1974    def deserialize_clan_banners(
1975        self, payload: typedefs.JSONObject
1976    ) -> collections.Sequence[clans.ClanBanner]:
1977        banners_seq: typing.MutableSequence[clans.ClanBanner] = []
1978        if banners := payload.get("clanBannerDecals"):
1979            for k, v in banners.items():
1980                banner_obj = clans.ClanBanner(
1981                    id=int(k),
1982                    foreground=assets.Image(v["foregroundPath"]),
1983                    background=assets.Image(v["backgroundPath"]),
1984                )
1985                banners_seq.append(banner_obj)
1986        return banners_seq

Deserialize a JSON array of a clan banners information.

Parameters
Returns
def deserialize_public_milestone_content( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.milestones.MilestoneContent:
1988    def deserialize_public_milestone_content(
1989        self, payload: typedefs.JSONObject
1990    ) -> milestones.MilestoneContent:
1991        items_categories: milestones.MilestoneItems | None = None
1992
1993        if raw_categories := payload.get("itemCategories"):
1994            for item in raw_categories:
1995                title: str | None = None
1996                if raw_title := item.get("title"):
1997                    title = raw_title
1998                if raw_hashes := item.get("itemHashes"):
1999                    hashes = raw_hashes
2000
2001                items_categories = milestones.MilestoneItems(title=title, hashes=hashes)
2002
2003        tips: typing.MutableSequence[str] = []
2004        if raw_tips := payload.get("tips"):
2005            tips = raw_tips
2006
2007        return milestones.MilestoneContent(
2008            about=typedefs.unknown(payload["about"]),
2009            status=typedefs.unknown(payload["status"]),
2010            tips=tips,
2011            items=items_categories,
2012        )

Deserialize a JSON payload of milestone content information.

Parameters
Returns
def deserialize_friend( self, payload: collections.abc.Mapping[str, typing.Any], /) -> aiobungie.crates.friends.Friend:
2014    def deserialize_friend(self, payload: typedefs.JSONObject, /) -> friends.Friend:
2015        bungie_user: user.BungieUser | None = None
2016
2017        if raw_bungie_user := payload.get("bungieNetUser"):
2018            bungie_user = self.deserialize_bungie_user(raw_bungie_user)
2019
2020        return friends.Friend(
2021            net=self._net,
2022            id=int(payload["lastSeenAsMembershipId"]),
2023            name=typedefs.unknown(payload["bungieGlobalDisplayName"]),
2024            code=payload.get("bungieGlobalDisplayNameCode"),
2025            relationship=enums.Relationship(payload["relationship"]),
2026            user=bungie_user,
2027            online_status=enums.Presence(payload["onlineStatus"]),
2028            online_title=payload["onlineTitle"],
2029            type=enums.MembershipType(payload["lastSeenAsBungieMembershipType"]),
2030        )

Deserialize a JSON payload of a Bungie friend information.

Parameters
Returns
def deserialize_friends( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.friends.Friend]:
2032    def deserialize_friends(
2033        self, payload: typedefs.JSONObject
2034    ) -> collections.Sequence[friends.Friend]:
2035        mut_seq: typing.MutableSequence[friends.Friend] = []
2036        if raw_friends := payload.get("friends"):
2037            for friend in raw_friends:
2038                mut_seq.append(self.deserialize_friend(friend))
2039        return mut_seq

Deserialize a JSON sequence of Bungie friends information.

This is usually used to deserialize the incoming/outgoing friend requests.

Parameters
Returns
def deserialize_friend_requests( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.friends.FriendRequestView:
2041    def deserialize_friend_requests(
2042        self, payload: typedefs.JSONObject
2043    ) -> friends.FriendRequestView:
2044        incoming: typing.MutableSequence[friends.Friend] = []
2045        outgoing: typing.MutableSequence[friends.Friend] = []
2046
2047        if raw_incoming_requests := payload.get("incomingRequests"):
2048            for incoming_request in raw_incoming_requests:
2049                incoming.append(self.deserialize_friend(incoming_request))
2050
2051        if raw_outgoing_requests := payload.get("outgoingRequests"):
2052            for outgoing_request in raw_outgoing_requests:
2053                outgoing.append(self.deserialize_friend(outgoing_request))
2054
2055        return friends.FriendRequestView(incoming=incoming, outgoing=outgoing)

Deserialize a JSON sequence of Bungie friend requests information.

This is used for incoming/outgoing friend requests.

Parameters
Returns
def deserialize_fireteams( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]:
2086    def deserialize_fireteams(
2087        self, payload: typedefs.JSONObject
2088    ) -> collections.Sequence[fireteams.Fireteam]:
2089        if "results" in payload:
2090            fireteams_ = [
2091                self._set_fireteam_fields(
2092                    elem, total_results=int(payload["totalResults"])
2093                )
2094                for elem in payload["results"]
2095            ]
2096        else:
2097            fireteams_ = []
2098        return fireteams_

Deserialize a JSON sequence of Bungie fireteams information.

Parameters
Returns
def deserialize_fireteam_destiny_users( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.fireteams.FireteamUser:
2100    def deserialize_fireteam_destiny_users(
2101        self, payload: typedefs.JSONObject
2102    ) -> fireteams.FireteamUser:
2103        destiny_obj = self.deserialize_destiny_membership(payload)
2104        return fireteams.FireteamUser(
2105            net=self._net,
2106            id=destiny_obj.id,
2107            code=destiny_obj.code,
2108            icon=destiny_obj.icon,
2109            types=destiny_obj.types,
2110            type=destiny_obj.type,
2111            is_public=destiny_obj.is_public,
2112            crossave_override=destiny_obj.crossave_override,
2113            name=destiny_obj.name,
2114            last_seen_name=destiny_obj.last_seen_name,
2115            fireteam_display_name=payload["FireteamDisplayName"],
2116            fireteam_membership_id=enums.MembershipType(
2117                payload["FireteamMembershipType"]
2118            ),
2119        )

Deserialize a JSON payload of Bungie fireteam destiny users information.

Parameters
Returns
def deserialize_fireteam_members( self, payload: collections.abc.Mapping[str, typing.Any], *, alternatives: bool = False) -> collections.abc.Sequence[aiobungie.crates.fireteams.FireteamMember]:
2121    def deserialize_fireteam_members(
2122        self, payload: typedefs.JSONObject, *, alternatives: bool = False
2123    ) -> collections.Sequence[fireteams.FireteamMember]:
2124        members_: list[fireteams.FireteamMember] = []
2125        if members := payload.get("Members" if not alternatives else "Alternates"):
2126            for member in members:
2127                bungie_fields = self.deserialize_partial_bungie_user(member)
2128                members_fields = fireteams.FireteamMember(
2129                    destiny_user=self.deserialize_fireteam_destiny_users(member),
2130                    has_microphone=member["hasMicrophone"],
2131                    character_id=int(member["characterId"]),
2132                    date_joined=time.clean_date(member["dateJoined"]),
2133                    last_platform_invite_date=time.clean_date(
2134                        member["lastPlatformInviteAttemptDate"]
2135                    ),
2136                    last_platform_invite_result=int(
2137                        member["lastPlatformInviteAttemptResult"]
2138                    ),
2139                    net=self._net,
2140                    name=bungie_fields.name,
2141                    id=bungie_fields.id,
2142                    icon=bungie_fields.icon,
2143                    is_public=bungie_fields.is_public,
2144                    crossave_override=bungie_fields.crossave_override,
2145                    types=bungie_fields.types,
2146                    type=bungie_fields.type,
2147                )
2148                members_.append(members_fields)
2149        return members_

Deserialize a JSON sequence of Bungie fireteam members information.

Parameters
  • payload (aiobungie.typedefs.JSONObject): The JSON payload.
  • alternatives (bool): If set to True, Then it will deserialize the alternatives data in the payload. If not the it will just deserialize the members data.
Returns
def deserialize_available_fireteam( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.fireteams.AvailableFireteam:
2151    def deserialize_available_fireteam(
2152        self, payload: typedefs.JSONObject
2153    ) -> fireteams.AvailableFireteam:
2154        fields = self._set_fireteam_fields(payload["Summary"])
2155        return fireteams.AvailableFireteam(
2156            id=fields.id,
2157            group_id=fields.group_id,
2158            platform=fields.platform,
2159            activity_type=fields.activity_type,
2160            is_immediate=fields.is_immediate,
2161            is_public=fields.is_public,
2162            is_valid=fields.is_valid,
2163            owner_id=fields.owner_id,
2164            player_slot_count=fields.player_slot_count,
2165            available_player_slots=fields.available_player_slots,
2166            available_alternate_slots=fields.available_alternate_slots,
2167            title=fields.title,
2168            date_created=fields.date_created,
2169            locale=fields.locale,
2170            last_modified=fields.last_modified,
2171            total_results=fields.total_results,
2172            scheduled_time=fields.scheduled_time,
2173            date_modified=fields.date_modified,
2174            members=self.deserialize_fireteam_members(payload),
2175            alternatives=self.deserialize_fireteam_members(payload, alternatives=True),
2176        )

Deserialize a JSON payload of a sequence of/fireteam information.

Parameters
Returns
  • An available fireteam object.
def deserialize_available_fireteams( self, data: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.fireteams.AvailableFireteam]:
2178    def deserialize_available_fireteams(
2179        self, data: typedefs.JSONObject
2180    ) -> collections.Sequence[fireteams.AvailableFireteam]:
2181        if raw_results := data.get("results"):
2182            fireteam_results: list[fireteams.AvailableFireteam] = [
2183                self.deserialize_available_fireteam(f) for f in raw_results
2184            ]
2185        else:
2186            fireteam_results = []
2187        return fireteam_results

Deserialize a JSON payload sequence of fireteam objects.

Parameters
Returns
  • collections.Sequence[aiobungie.crates.fireteams.AvailableFireteam]: A sequence of available fireteams.
def deserialize_fireteam_party( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.fireteams.FireteamParty:
2189    def deserialize_fireteam_party(
2190        self, payload: typedefs.JSONObject
2191    ) -> fireteams.FireteamParty:
2192        last_destination_hash: int | None = None
2193        if raw_dest_hash := payload.get("lastOrbitedDestinationHash"):
2194            last_destination_hash = int(raw_dest_hash)
2195
2196        return fireteams.FireteamParty(
2197            members=[
2198                self._deserialize_fireteam_party_member(member)
2199                for member in payload["partyMembers"]
2200            ],
2201            activity=self._deserialize_fireteam_party_current_activity(
2202                payload["currentActivity"]
2203            ),
2204            settings=self._deserialize_fireteam_party_settings(payload["joinability"]),
2205            last_destination_hash=last_destination_hash,
2206            tracking=payload["tracking"],
2207        )

Deserialize a JSON payload of profileTransitory component response.

Parameters
Returns
def deserialize_seasonal_artifact( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.season.Artifact:
2250    def deserialize_seasonal_artifact(
2251        self, payload: typedefs.JSONObject
2252    ) -> season.Artifact:
2253        if raw_artifact := payload.get("seasonalArtifact"):
2254            if points := raw_artifact.get("pointProgression"):
2255                points_prog = progressions.Progression(
2256                    hash=points["progressionHash"],
2257                    level=points["level"],
2258                    cap=points["levelCap"],
2259                    daily_limit=points["dailyLimit"],
2260                    weekly_limit=points["weeklyLimit"],
2261                    current_progress=points["currentProgress"],
2262                    daily_progress=points["dailyProgress"],
2263                    needed=points["progressToNextLevel"],
2264                    next_level=points["nextLevelAt"],
2265                )
2266
2267            if bonus := raw_artifact.get("powerBonusProgression"):
2268                power_bonus_prog = progressions.Progression(
2269                    hash=bonus["progressionHash"],
2270                    level=bonus["level"],
2271                    cap=bonus["levelCap"],
2272                    daily_limit=bonus["dailyLimit"],
2273                    weekly_limit=bonus["weeklyLimit"],
2274                    current_progress=bonus["currentProgress"],
2275                    daily_progress=bonus["dailyProgress"],
2276                    needed=bonus["progressToNextLevel"],
2277                    next_level=bonus["nextLevelAt"],
2278                )
2279            artifact = season.Artifact(
2280                hash=raw_artifact["artifactHash"],
2281                power_bonus=raw_artifact["powerBonus"],
2282                acquired_points=raw_artifact["pointsAcquired"],
2283                bonus=power_bonus_prog,
2284                points=points_prog,
2285            )
2286        return artifact

Deserialize a JSON payload of a Destiny 2 seasonal artifact information.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_profile_progression( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.profile.ProfileProgression:
2288    def deserialize_profile_progression(
2289        self, payload: typedefs.JSONObject
2290    ) -> profile.ProfileProgression:
2291        return profile.ProfileProgression(
2292            artifact=self.deserialize_seasonal_artifact(payload["data"]),
2293            checklist={
2294                int(check_id): checklists
2295                for check_id, checklists in payload["data"]["checklists"].items()
2296            },
2297        )

Deserialize a JSON payload of a profile progression component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_instanced_item( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.items.ItemInstance:
2299    def deserialize_instanced_item(
2300        self, payload: typedefs.JSONObject
2301    ) -> items.ItemInstance:
2302        damage_type_hash: int | None = None
2303        if raw_damagetype_hash := payload.get("damageTypeHash"):
2304            damage_type_hash = int(raw_damagetype_hash)
2305
2306        required_hashes: typing.Optional[collections.Collection[int]] = None
2307        if raw_required_hashes := payload.get("unlockHashesRequiredToEquip"):
2308            required_hashes = [int(raw_hash) for raw_hash in raw_required_hashes]
2309
2310        breaker_type: items.ItemBreakerType | None = None
2311        if raw_break_type := payload.get("breakerType"):
2312            breaker_type = items.ItemBreakerType(int(raw_break_type))
2313
2314        breaker_type_hash: int | None = None
2315        if raw_break_type_hash := payload.get("breakerTypeHash"):
2316            breaker_type_hash = int(raw_break_type_hash)
2317
2318        energy: items.ItemEnergy | None = None
2319        if raw_energy := payload.get("energy"):
2320            energy = self.deserialize_item_energy(raw_energy)
2321
2322        primary_stats = None
2323        if raw_primary_stats := payload.get("primaryStat"):
2324            primary_stats = self.deserialize_item_stats_view(raw_primary_stats)
2325
2326        return items.ItemInstance(
2327            damage_type=enums.DamageType(int(payload["damageType"])),
2328            damage_type_hash=damage_type_hash,
2329            primary_stat=primary_stats,
2330            item_level=int(payload["itemLevel"]),
2331            quality=int(payload["quality"]),
2332            is_equipped=payload["isEquipped"],
2333            can_equip=payload["canEquip"],
2334            equip_required_level=int(payload["equipRequiredLevel"]),
2335            required_equip_unlock_hashes=required_hashes,
2336            cant_equip_reason=int(payload["cannotEquipReason"]),
2337            breaker_type=breaker_type,
2338            breaker_type_hash=breaker_type_hash,
2339            energy=energy,
2340        )

Deserialize a JSON object into an instanced item.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_item_energy( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.items.ItemEnergy:
2342    def deserialize_item_energy(self, payload: typedefs.JSONObject) -> items.ItemEnergy:
2343        energy_hash: int | None = None
2344        if raw_energy_hash := payload.get("energyTypeHash"):
2345            energy_hash = int(raw_energy_hash)
2346
2347        return items.ItemEnergy(
2348            hash=energy_hash,
2349            type=items.ItemEnergyType(int(payload["energyType"])),
2350            capacity=int(payload["energyCapacity"]),
2351            used_energy=int(payload["energyUsed"]),
2352            unused_energy=int(payload["energyUnused"]),
2353        )
def deserialize_item_perk( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.items.ItemPerk:
2355    def deserialize_item_perk(self, payload: typedefs.JSONObject) -> items.ItemPerk:
2356        perk_hash: int | None = None
2357        if raw_perk_hash := payload.get("perkHash"):
2358            perk_hash = int(raw_perk_hash)
2359
2360        return items.ItemPerk(
2361            hash=perk_hash,
2362            icon=assets.Image(payload["iconPath"]),
2363            is_active=payload["isActive"],
2364            is_visible=payload["visible"],
2365        )
def deserialize_item_socket( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.items.ItemSocket:
2367    def deserialize_item_socket(self, payload: typedefs.JSONObject) -> items.ItemSocket:
2368        plug_hash: int | None = None
2369        if raw_plug_hash := payload.get("plugHash"):
2370            plug_hash = int(raw_plug_hash)
2371
2372        enable_fail_indexes: list[int] | None = None
2373        if raw_indexes := payload.get("enableFailIndexes"):
2374            enable_fail_indexes = [int(index) for index in raw_indexes]
2375
2376        return items.ItemSocket(
2377            plug_hash=plug_hash,
2378            is_enabled=payload["isEnabled"],
2379            enable_fail_indexes=enable_fail_indexes,
2380            is_visible=payload.get("visible"),
2381        )
def deserialize_item_stats_view( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.items.ItemStatsView:
2383    def deserialize_item_stats_view(
2384        self, payload: typedefs.JSONObject
2385    ) -> items.ItemStatsView:
2386        return items.ItemStatsView(
2387            stat_hash=payload.get("statHash"), value=payload.get("value")
2388        )
def deserialize_plug_item_state( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.items.PlugItemState:
2390    def deserialize_plug_item_state(
2391        self, payload: typedefs.JSONObject
2392    ) -> items.PlugItemState:
2393        item_hash: int | None = None
2394        if raw_item_hash := payload.get("plugItemHash"):
2395            item_hash = int(raw_item_hash)
2396
2397        insert_fail_indexes: list[int] | None = None
2398        if raw_fail_indexes := payload.get("insertFailIndexes"):
2399            insert_fail_indexes = [int(k) for k in raw_fail_indexes]
2400
2401        enable_fail_indexes: list[int] | None = None
2402        if raw_enabled_indexes := payload.get("enableFailIndexes"):
2403            enable_fail_indexes = [int(k) for k in raw_enabled_indexes]
2404
2405        return items.PlugItemState(
2406            item_hash=item_hash,
2407            insert_fail_indexes=insert_fail_indexes,
2408            enable_fail_indexes=enable_fail_indexes,
2409            is_enabled=payload["enabled"],
2410            can_insert=payload["canInsert"],
2411        )
@typing.final
class FireteamActivity(builtins.int, aiobungie.Enum):
 65@typing.final
 66class FireteamActivity(int, enums.Enum):
 67    """An enum for the fireteam activities."""
 68
 69    ALL = 0
 70    CRUCIBLE = 2
 71    TRIALS_OF_OSIRIS = 3
 72    NIGHTFALL = 4
 73    ANY = 5
 74    GAMBIT = 6
 75    BLIND_WELL = 7
 76    NIGHTMARE_HUNTS = 12
 77    ALTARS_OF_SORROWS = 14
 78    DUNGEON = 15
 79    RAID_LW = 20
 80    RAID_GOS = 21
 81    RAID_DSC = 22
 82    EXO_CHALLENGE = 23
 83    S12_WRATHBORN = 24
 84    EMPIRE_HUNTS = 25
 85    S13_BATTLEGROUNDS = 26
 86    EXOTIC_QUEST = 27
 87    RAID_VOG = 28
 88    S14_EXPUNGE = 30
 89    S15_ASTRAL_ALIGNMENT = 31
 90    S15_SHATTERED_RELAM = 32
 91    SHATTERED_THRONE = 33
 92    PROPHECY = 34
 93    PIT_OF_HERESY = 35
 94    DOE = 36
 95    """Dares of Eternity."""
 96    DUNGEON_GOA = 37
 97    """Grasp of Avarice."""
 98    VOW_OF_THE_DISCPILE = 38
 99    CAMPAIGN = 39
100    WELLSPRING = 40
101    S16_BATTLEGROUNDS = 41
102    S17_NIGHTMARE_CONTAINMENT = 44
103    S17_SEVER = 45

An enum for the fireteam activities.

CRUCIBLE = <FireteamActivity.CRUCIBLE: 2>
TRIALS_OF_OSIRIS = <FireteamActivity.TRIALS_OF_OSIRIS: 3>
NIGHTFALL = <FireteamActivity.NIGHTFALL: 4>
GAMBIT = <FireteamActivity.GAMBIT: 6>
BLIND_WELL = <FireteamActivity.BLIND_WELL: 7>
NIGHTMARE_HUNTS = <FireteamActivity.NIGHTMARE_HUNTS: 12>
ALTARS_OF_SORROWS = <FireteamActivity.ALTARS_OF_SORROWS: 14>
DUNGEON = <FireteamActivity.DUNGEON: 15>
RAID_LW = <FireteamActivity.RAID_LW: 20>
RAID_GOS = <FireteamActivity.RAID_GOS: 21>
RAID_DSC = <FireteamActivity.RAID_DSC: 22>
EXO_CHALLENGE = <FireteamActivity.EXO_CHALLENGE: 23>
S12_WRATHBORN = <FireteamActivity.S12_WRATHBORN: 24>
EMPIRE_HUNTS = <FireteamActivity.EMPIRE_HUNTS: 25>
S13_BATTLEGROUNDS = <FireteamActivity.S13_BATTLEGROUNDS: 26>
EXOTIC_QUEST = <FireteamActivity.EXOTIC_QUEST: 27>
RAID_VOG = <FireteamActivity.RAID_VOG: 28>
S14_EXPUNGE = <FireteamActivity.S14_EXPUNGE: 30>
S15_ASTRAL_ALIGNMENT = <FireteamActivity.S15_ASTRAL_ALIGNMENT: 31>
S15_SHATTERED_RELAM = <FireteamActivity.S15_SHATTERED_RELAM: 32>
SHATTERED_THRONE = <FireteamActivity.SHATTERED_THRONE: 33>
PROPHECY = <FireteamActivity.PROPHECY: 34>
PIT_OF_HERESY = <FireteamActivity.PIT_OF_HERESY: 35>
DOE = <FireteamActivity.DOE: 36>

Dares of Eternity.

DUNGEON_GOA = <FireteamActivity.DUNGEON_GOA: 37>

Grasp of Avarice.

VOW_OF_THE_DISCPILE = <FireteamActivity.VOW_OF_THE_DISCPILE: 38>
CAMPAIGN = <FireteamActivity.CAMPAIGN: 39>
WELLSPRING = <FireteamActivity.WELLSPRING: 40>
S16_BATTLEGROUNDS = <FireteamActivity.S16_BATTLEGROUNDS: 41>
S17_NIGHTMARE_CONTAINMENT = <FireteamActivity.S17_NIGHTMARE_CONTAINMENT: 44>
S17_SEVER = <FireteamActivity.S17_SEVER: 45>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class FireteamDate(builtins.int, aiobungie.Enum):
129@typing.final
130class FireteamDate(int, enums.Enum):
131    """An enum for fireteam date ranges."""
132
133    ALL = 0
134    NOW = 1
135    TODAY = 2
136    TWO_DAYS = 3
137    THIS_WEEK = 4

An enum for fireteam date ranges.

ALL = <FireteamDate.ALL: 0>
NOW = <FireteamDate.NOW: 1>
TODAY = <FireteamDate.TODAY: 2>
TWO_DAYS = <FireteamDate.TWO_DAYS: 3>
THIS_WEEK = <FireteamDate.THIS_WEEK: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class FireteamLanguage(builtins.str, aiobungie.Enum):
106@typing.final
107class FireteamLanguage(str, enums.Enum):
108    """An enum for fireteams languages filters."""
109
110    ALL = ""
111    ENGLISH = "en"
112    FRENCH = "fr"
113    ESPANOL = "es"
114    DEUTSCH = "de"
115    ITALIAN = "it"
116    JAPANESE = "ja"
117    PORTUGUESE = "pt-br"
118    RUSSIAN = "ru"
119    POLISH = "pl"
120    KOREAN = "ko"
121    # ? China
122    ZH_CHT = "zh-cht"
123    ZH_CHS = "zh-chs"
124
125    def __str__(self) -> str:
126        return str(self.value)

An enum for fireteams languages filters.

ENGLISH = <FireteamLanguage.ENGLISH: en>
FRENCH = <FireteamLanguage.FRENCH: fr>
ESPANOL = <FireteamLanguage.ESPANOL: es>
DEUTSCH = <FireteamLanguage.DEUTSCH: de>
ITALIAN = <FireteamLanguage.ITALIAN: it>
JAPANESE = <FireteamLanguage.JAPANESE: ja>
PORTUGUESE = <FireteamLanguage.PORTUGUESE: pt-br>
RUSSIAN = <FireteamLanguage.RUSSIAN: ru>
POLISH = <FireteamLanguage.POLISH: pl>
KOREAN = <FireteamLanguage.KOREAN: ko>
ZH_CHT = <FireteamLanguage.ZH_CHT: zh-cht>
ZH_CHS = <FireteamLanguage.ZH_CHS: zh-chs>
Inherited Members
Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
@typing.final
class FireteamPlatform(builtins.int, aiobungie.Enum):
52@typing.final
53class FireteamPlatform(int, enums.Enum):
54    """An enum for fireteam related to bungie fireteams.
55    This is different from the normal `aiobungie.MembershipType`.
56    """
57
58    ANY = 0
59    PSN_NETWORK = 1
60    XBOX_LIVE = 2
61    STEAM = 4
62    STADIA = 5

An enum for fireteam related to bungie fireteams. This is different from the normal aiobungie.MembershipType.

PSN_NETWORK = <FireteamPlatform.PSN_NETWORK: 1>
XBOX_LIVE = <FireteamPlatform.XBOX_LIVE: 2>
STEAM = <FireteamPlatform.STEAM: 4>
STADIA = <FireteamPlatform.STADIA: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Flag(enum.Flag):
 90class Flag(__enum.Flag):
 91    """Builtin Python enum flag with extra handlings."""
 92
 93    # Needs to type this here for mypy
 94    _value_: int
 95
 96    @property
 97    def name(self) -> str:  # type: ignore[override]
 98        if self._name_ is None:
 99            self._name_ = f"UNKNOWN {self._value_}"
100
101        return self._name_
102
103    @property
104    def value(self) -> int:  # type: ignore[override]
105        return self._value_
106
107    def __str__(self) -> str:
108        return self.name
109
110    def __repr__(self) -> str:
111        return f"<{type(self).__name__}.{self.name}: {self._value_!s}>"
112
113    def __int__(self) -> int:
114        return int(self.value)
115
116    def __or__(self, other: Flag | int) -> Flag:
117        return self.__class__(self._value_ | int(other))
118
119    def __xor__(self, other: Flag | int) -> Flag:
120        return self.__class__(self._value_ ^ int(other))
121
122    def __and__(self, other: Flag | int) -> Flag:
123        return self.__class__(other & int(other))
124
125    def __invert__(self) -> Flag:
126        return self.__class__(~self._value_)
127
128    def __contains__(self, other: Flag | int) -> bool:
129        return self.value & int(other) == int(other)

Builtin Python enum flag with extra handlings.

name: str

The name of the Enum member.

value: int

The value of the Enum member.

@attrs.define(auto_exc=True)
class Forbidden(aiobungie.HTTPException):
137@attrs.define(auto_exc=True)
138class Forbidden(HTTPException):
139    """Exception that's raised for when status code 403 occurs."""
140
141    http_status: http.HTTPStatus = attrs.field(
142        default=http.HTTPStatus.FORBIDDEN, init=False
143    )

Exception that's raised for when status code 403 occurs.

Forbidden( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class Forbidden.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class GameMode(builtins.int, aiobungie.Enum):
258@typing.final
259class GameMode(int, Enum):
260    """An Enum for all available gamemodes in Destiny 2."""
261
262    NONE = 0
263    STORY = 2
264    STRIKE = 3
265    RAID = 4
266    ALLPVP = 5
267    PATROL = 6
268    ALLPVE = 7
269    RESERVED9 = 9
270    CONTROL = 10
271    RESERVED11 = 11
272    CLASH = 12
273    RESERVED13 = 13
274    CRIMSONDOUBLES = 15
275    NIGHTFALL = 16
276    HEROICNIGHTFALL = 17
277    ALLSTRIKES = 18
278    IRONBANNER = 19
279    RESERVED20 = 20
280    RESERVED21 = 21
281    RESERVED22 = 22
282    RESERVED24 = 24
283    ALLMAYHEM = 25
284    RESERVED26 = 26
285    RESERVED27 = 27
286    RESERVED28 = 28
287    RESERVED29 = 29
288    RESERVED30 = 30
289    SUPREMACY = 31
290    PRIVATEMATCHESALL = 32
291    SURVIVAL = 37
292    COUNTDOWN = 38
293    TRIALSOFTHENINE = 39
294    SOCIAL = 40
295    TRIALSCOUNTDOWN = 41
296    TRIALSSURVIVAL = 42
297    IRONBANNERCONTROL = 43
298    IRONBANNERCLASH = 44
299    IRONBANNERSUPREMACY = 45
300    SCOREDNIGHTFALL = 46
301    SCOREDHEROICNIGHTFALL = 47
302    RUMBLE = 48
303    ALLDOUBLES = 49
304    DOUBLES = 50
305    PRIVATEMATCHESCLASH = 51
306    PRIVATEMATCHESCONTROL = 52
307    PRIVATEMATCHESSUPREMACY = 53
308    PRIVATEMATCHESCOUNTDOWN = 54
309    PRIVATEMATCHESSURVIVAL = 55
310    PRIVATEMATCHESMAYHEM = 56
311    PRIVATEMATCHESRUMBLE = 57
312    HEROICADVENTURE = 58
313    SHOWDOWN = 59
314    LOCKDOWN = 60
315    SCORCHED = 61
316    SCORCHEDTEAM = 62
317    GAMBIT = 63
318    ALLPVECOMPETITIVE = 64
319    BREAKTHROUGH = 65
320    BLACKARMORYRUN = 66
321    SALVAGE = 67
322    IRONBANNERSALVAGE = 68
323    PVPCOMPETITIVE = 69
324    PVPQUICKPLAY = 70
325    CLASHQUICKPLAY = 71
326    CLASHCOMPETITIVE = 72
327    CONTROLQUICKPLAY = 73
328    CONTROLCOMPETITIVE = 74
329    GAMBITPRIME = 75
330    RECKONING = 76
331    MENAGERIE = 77
332    VEXOFFENSIVE = 78
333    NIGHTMAREHUNT = 79
334    ELIMINATION = 80
335    MOMENTUM = 81
336    DUNGEON = 82
337    SUNDIAL = 83
338    TRIALS_OF_OSIRIS = 84
339    DARES = 85
340    OFFENSIVE = 86
341    LOSTSECTOR = 87
342    RIFT = 88
343    ZONECONTROL = 89
344    IRONBANNERRIFT = 90

An Enum for all available gamemodes in Destiny 2.

NONE = <GameMode.NONE: 0>
STORY = <GameMode.STORY: 2>
STRIKE = <GameMode.STRIKE: 3>
RAID = <GameMode.RAID: 4>
ALLPVP = <GameMode.ALLPVP: 5>
PATROL = <GameMode.PATROL: 6>
ALLPVE = <GameMode.ALLPVE: 7>
RESERVED9 = <GameMode.RESERVED9: 9>
CONTROL = <GameMode.CONTROL: 10>
RESERVED11 = <GameMode.RESERVED11: 11>
CLASH = <GameMode.CLASH: 12>
RESERVED13 = <GameMode.RESERVED13: 13>
CRIMSONDOUBLES = <GameMode.CRIMSONDOUBLES: 15>
NIGHTFALL = <GameMode.NIGHTFALL: 16>
HEROICNIGHTFALL = <GameMode.HEROICNIGHTFALL: 17>
ALLSTRIKES = <GameMode.ALLSTRIKES: 18>
IRONBANNER = <GameMode.IRONBANNER: 19>
RESERVED20 = <GameMode.RESERVED20: 20>
RESERVED21 = <GameMode.RESERVED21: 21>
RESERVED22 = <GameMode.RESERVED22: 22>
RESERVED24 = <GameMode.RESERVED24: 24>
ALLMAYHEM = <GameMode.ALLMAYHEM: 25>
RESERVED26 = <GameMode.RESERVED26: 26>
RESERVED27 = <GameMode.RESERVED27: 27>
RESERVED28 = <GameMode.RESERVED28: 28>
RESERVED29 = <GameMode.RESERVED29: 29>
RESERVED30 = <GameMode.RESERVED30: 30>
SUPREMACY = <GameMode.SUPREMACY: 31>
PRIVATEMATCHESALL = <GameMode.PRIVATEMATCHESALL: 32>
SURVIVAL = <GameMode.SURVIVAL: 37>
COUNTDOWN = <GameMode.COUNTDOWN: 38>
TRIALSOFTHENINE = <GameMode.TRIALSOFTHENINE: 39>
SOCIAL = <GameMode.SOCIAL: 40>
TRIALSCOUNTDOWN = <GameMode.TRIALSCOUNTDOWN: 41>
TRIALSSURVIVAL = <GameMode.TRIALSSURVIVAL: 42>
IRONBANNERCONTROL = <GameMode.IRONBANNERCONTROL: 43>
IRONBANNERCLASH = <GameMode.IRONBANNERCLASH: 44>
IRONBANNERSUPREMACY = <GameMode.IRONBANNERSUPREMACY: 45>
SCOREDNIGHTFALL = <GameMode.SCOREDNIGHTFALL: 46>
SCOREDHEROICNIGHTFALL = <GameMode.SCOREDHEROICNIGHTFALL: 47>
RUMBLE = <GameMode.RUMBLE: 48>
ALLDOUBLES = <GameMode.ALLDOUBLES: 49>
DOUBLES = <GameMode.DOUBLES: 50>
PRIVATEMATCHESCLASH = <GameMode.PRIVATEMATCHESCLASH: 51>
PRIVATEMATCHESCONTROL = <GameMode.PRIVATEMATCHESCONTROL: 52>
PRIVATEMATCHESSUPREMACY = <GameMode.PRIVATEMATCHESSUPREMACY: 53>
PRIVATEMATCHESCOUNTDOWN = <GameMode.PRIVATEMATCHESCOUNTDOWN: 54>
PRIVATEMATCHESSURVIVAL = <GameMode.PRIVATEMATCHESSURVIVAL: 55>
PRIVATEMATCHESMAYHEM = <GameMode.PRIVATEMATCHESMAYHEM: 56>
PRIVATEMATCHESRUMBLE = <GameMode.PRIVATEMATCHESRUMBLE: 57>
HEROICADVENTURE = <GameMode.HEROICADVENTURE: 58>
SHOWDOWN = <GameMode.SHOWDOWN: 59>
LOCKDOWN = <GameMode.LOCKDOWN: 60>
SCORCHED = <GameMode.SCORCHED: 61>
SCORCHEDTEAM = <GameMode.SCORCHEDTEAM: 62>
GAMBIT = <GameMode.GAMBIT: 63>
ALLPVECOMPETITIVE = <GameMode.ALLPVECOMPETITIVE: 64>
BREAKTHROUGH = <GameMode.BREAKTHROUGH: 65>
BLACKARMORYRUN = <GameMode.BLACKARMORYRUN: 66>
SALVAGE = <GameMode.SALVAGE: 67>
IRONBANNERSALVAGE = <GameMode.IRONBANNERSALVAGE: 68>
PVPCOMPETITIVE = <GameMode.PVPCOMPETITIVE: 69>
PVPQUICKPLAY = <GameMode.PVPQUICKPLAY: 70>
CLASHQUICKPLAY = <GameMode.CLASHQUICKPLAY: 71>
CLASHCOMPETITIVE = <GameMode.CLASHCOMPETITIVE: 72>
CONTROLQUICKPLAY = <GameMode.CONTROLQUICKPLAY: 73>
CONTROLCOMPETITIVE = <GameMode.CONTROLCOMPETITIVE: 74>
GAMBITPRIME = <GameMode.GAMBITPRIME: 75>
RECKONING = <GameMode.RECKONING: 76>
MENAGERIE = <GameMode.MENAGERIE: 77>
VEXOFFENSIVE = <GameMode.VEXOFFENSIVE: 78>
NIGHTMAREHUNT = <GameMode.NIGHTMAREHUNT: 79>
ELIMINATION = <GameMode.ELIMINATION: 80>
MOMENTUM = <GameMode.MOMENTUM: 81>
DUNGEON = <GameMode.DUNGEON: 82>
SUNDIAL = <GameMode.SUNDIAL: 83>
TRIALS_OF_OSIRIS = <GameMode.TRIALS_OF_OSIRIS: 84>
DARES = <GameMode.DARES: 85>
OFFENSIVE = <GameMode.OFFENSIVE: 86>
LOSTSECTOR = <GameMode.LOSTSECTOR: 87>
RIFT = <GameMode.RIFT: 88>
ZONECONTROL = <GameMode.ZONECONTROL: 89>
IRONBANNERRIFT = <GameMode.IRONBANNERRIFT: 90>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class GatingScope(builtins.int, aiobungie.Enum):
57@typing.final
58class GatingScope(int, enums.Enum):
59    """An enum represents restrictive type of gating that is being performed by an entity.
60
61    This is useful as a shortcut to avoid a lot of lookups when determining whether the gating on an Entity
62    applies to everyone equally, or to their specific Profile or Character states.
63    """
64
65    NONE = 0
66    GLOBAL = 1
67    CLAN = 2
68    PROFILE = 3
69    CHARACTER = 4
70    ITEM = 5
71    ASSUMED_WORST_CASE = 6

An enum represents restrictive type of gating that is being performed by an entity.

This is useful as a shortcut to avoid a lot of lookups when determining whether the gating on an Entity applies to everyone equally, or to their specific Profile or Character states.

NONE = <GatingScope.NONE: 0>
GLOBAL = <GatingScope.GLOBAL: 1>
CLAN = <GatingScope.CLAN: 2>
PROFILE = <GatingScope.PROFILE: 3>
CHARACTER = <GatingScope.CHARACTER: 4>
ITEM = <GatingScope.ITEM: 5>
ASSUMED_WORST_CASE = <GatingScope.ASSUMED_WORST_CASE: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Gender(builtins.int, aiobungie.Enum):
475@typing.final
476class Gender(int, Enum):
477    """An Enum for Destiny Genders."""
478
479    MALE = 0
480    FEMALE = 1
481    UNKNOWN = 2

An Enum for Destiny Genders.

MALE = <Gender.MALE: 0>
FEMALE = <Gender.FEMALE: 1>
UNKNOWN = <Gender.UNKNOWN: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class GroupType(builtins.int, aiobungie.Enum):
644@typing.final
645class GroupType(int, Enum):
646    """An enums for the known bungie group types."""
647
648    GENERAL = 0
649    CLAN = 1

An enums for the known bungie group types.

GENERAL = <GroupType.GENERAL: 0>
CLAN = <GroupType.CLAN: 1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class HTTPError(aiobungie.AiobungieError):
79@attrs.define(auto_exc=True)
80class HTTPError(AiobungieError):
81    """Base HTTP request errors exception."""
82
83    message: str
84    """The error message."""
85
86    http_status: http.HTTPStatus
87    """The response status."""

Base HTTP request errors exception.

HTTPError(message: str, http_status: http.HTTPStatus)
2def __init__(self, message, http_status):
3    self.message = message
4    self.http_status = http_status
5    BaseException.__init__(self, self.message,self.http_status)

Method generated by attrs for class HTTPError.

message: str

The error message.

http_status: http.HTTPStatus

The response status.

Inherited Members
builtins.BaseException
with_traceback
args
@attrs.define(auto_exc=True, kw_only=True)
class HTTPException(aiobungie.HTTPError):
 90@attrs.define(auto_exc=True, kw_only=True)
 91class HTTPException(HTTPError):
 92    """An in-depth HTTP exception that's raised with more information."""
 93
 94    error_code: int
 95    """The returned Bungie error status code."""
 96
 97    http_status: http.HTTPStatus
 98    """The request response http status."""
 99
100    throttle_seconds: int
101    """The Bungie response throttle seconds."""
102
103    url: typedefs.StrOrURL | None
104    """The URL/endpoint caused this error."""
105
106    body: typing.Any
107    """The response body."""
108
109    headers: multidict.CIMultiDictProxy[str]
110    """The response headers."""
111
112    message: str
113    """A Bungie human readable message describes the cause of the error."""
114
115    error_status: str
116    """A Bungie short error status describes the cause of the error."""
117
118    message_data: dict[str, str]
119    """A dict of string key, value that includes each cause of the error
120    to a message describes information about that error.
121    """
122
123    def __str__(self) -> str:
124        if self.message:
125            message_body = self.message
126
127        if self.error_status:
128            error_status_body = self.error_status
129
130        return (
131            f"{self.http_status.name.replace('_', '').title()} {self.http_status.value}: "
132            f"Error status: {error_status_body}, Error message: {message_body} from {self.url} "
133            f"{str(self.body)}"
134        )

An in-depth HTTP exception that's raised with more information.

HTTPException( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class HTTPException.

error_code: int

The returned Bungie error status code.

http_status: http.HTTPStatus

The request response http status.

throttle_seconds: int

The Bungie response throttle seconds.

url: Union[str, yarl.URL, NoneType]

The URL/endpoint caused this error.

body: Any

The response body.

headers: multidict._multidict.CIMultiDictProxy[str]

The response headers.

message: str

A Bungie human readable message describes the cause of the error.

error_status: str

A Bungie short error status describes the cause of the error.

message_data: dict[str, str]

A dict of string key, value that includes each cause of the error to a message describes information about that error.

Inherited Members
builtins.BaseException
with_traceback
args
class Image:
 73class Image:
 74    """Representation of an image/avatar/picture at Bungie.
 75
 76    Example
 77    -------
 78    ```py
 79    from aiobungie import Image
 80    img = Image("img/destiny_content/pgcr/raid_eclipse.jpg")
 81    print(img)
 82    # https://www.bungie.net/img/destiny_content/pgcr/raid_eclipse.jpg
 83
 84    # Stream the image.
 85    async for chunk in img:
 86        # Byte chunks of the image.
 87        print(chunk)
 88
 89    # Save the image to a file.
 90    await img.save("file_name", "/my/path/to/save/to", "jpeg")
 91    ```
 92
 93    Parameters
 94    ----------
 95    path : `str | None`
 96        The path to the image..
 97    """
 98
 99    __slots__ = ("_path",)
100
101    def __init__(self, path: str) -> None:
102        self._path = path
103
104    @property
105    def is_missing(self) -> bool:
106        return not self._path
107
108    @property
109    def url(self) -> str:
110        """The URL to the image."""
111        return self.create_url()
112
113    @staticmethod
114    def default() -> str:
115        """Returns the path to the missing Bungie image.
116
117        Note
118        ----
119        This returns the path only, If you want an actual image object use `Image.default_or_else()`
120        """
121        return "/img/misc/missing_icon_d2.png"
122
123    @classmethod
124    def default_or_else(cls, path: str | None = None) -> Self:
125        """Return the default image if `path` was `None` otherwise an `Image` object.
126
127        Example
128        -------
129        ```py
130        img = Image.default_or_else(None)
131        print(img.url()) # https://www.bungie.net/img/misc/missing_icon_d2.png
132
133        img = Image.default_or_else("/some_path/image.png")
134        ```
135        """
136        return cls(path or Image.default())
137
138    def create_url(self) -> str:
139        """Creates a full URL to the image path.
140
141        Returns
142        -------
143        str
144            The URL to the image.
145        """
146        return f"{url.BASE}/{self._path if self._path else self.default()}"
147
148    async def save(
149        self,
150        file_name: str,
151        path: pathlib.Path | str,
152        /,
153        mime_type: MimeType | str = MimeType.JPEG,
154    ) -> None:
155        """Saves the image to a file.
156
157        Parameters
158        ----------
159        file_name : `str`
160            A name for the file to save the image to.
161        path : `pathlib.Path | str`
162            A path tp save the image to.
163
164        Other Parameters
165        ----------------
166        mime_type : `MimeType | str`
167            MIME type of the image. Defaults to JPEG.
168
169        Raises
170        ------
171        `FileNotFoundError`
172            If the path provided does not exist.
173        `RuntimeError`
174            If the image could not be saved.
175        `PermissionError`
176            If the path provided is not writable or does not have write permissions.
177        """
178        if isinstance(path, pathlib.Path) and not path.exists():
179            raise FileNotFoundError(f"File does not exist: {path!r}")
180
181        if self.is_missing:
182            return
183
184        path = pathlib.Path(path)
185
186        loop = helpers.get_or_make_loop()
187        pool = concurrent.futures.ThreadPoolExecutor()
188
189        try:
190            with pool:
191                await loop.run_in_executor(
192                    pool, _write, path, file_name, mime_type, await self.read()
193                )
194                _LOGGER.info("Saved image to %s", file_name)
195
196        except asyncio.CancelledError:
197            pass
198
199        except Exception as err:
200            raise RuntimeError("Encountered an error while saving image.") from err
201
202    async def read(self) -> bytes:
203        """Read this image bytes.
204
205        Returns
206        -------
207        `bytes`
208            The bytes of this image.
209        """
210        client_session = aiohttp.ClientSession()
211
212        try:
213            await client_session.__aenter__()
214            response = await client_session.get(self.create_url())
215
216            if 300 >= response.status >= 200:
217                reader = await response.read()
218
219        except Exception as exc:
220            raise RuntimeError(f"Failed to read image: {exc}") from None
221        finally:
222            await client_session.__aexit__(None, None, None)
223        return reader
224
225    async def iter(self) -> collections.AsyncGenerator[bytes, None]:
226        """Iterates over the image bytes lazily.
227
228        Example
229        -------
230        import aiobungie
231
232        resource = aiobungie.Image("img/misc/missing_icon_d2.png")
233        async for chunk in resource.iter():
234            print(chunk)
235
236        Returns
237        -------
238        `collections.AsyncGenerator[bytes, None]`
239            An async generator of the image bytes.
240        """
241
242        async for chunk in self:
243            yield chunk
244
245    def __eq__(self, __value: object) -> bool:
246        if not isinstance(__value, Image):
247            return NotImplemented
248        return self._path == __value._path
249
250    def __ne__(self, __value: object) -> bool:
251        return not self.__eq__(__value)
252
253    def __repr__(self) -> str:
254        return f"Image(url={self.create_url()})"
255
256    def __str__(self) -> str:
257        return self.create_url()
258
259    def __aiter__(self) -> Image:
260        return self
261
262    async def __anext__(self) -> bytes:
263        return await self.read()
264
265    def __await__(self) -> collections.Generator[None, None, bytes]:
266        return self.__anext__().__await__()

Representation of an image/avatar/picture at Bungie.

Example
from aiobungie import Image
img = Image("img/destiny_content/pgcr/raid_eclipse.jpg")
print(img)
# https://www.bungie.net/img/destiny_content/pgcr/raid_eclipse.jpg

# Stream the image.
async for chunk in img:
    # Byte chunks of the image.
    print(chunk)

# Save the image to a file.
await img.save("file_name", "/my/path/to/save/to", "jpeg")
Parameters
  • path (str | None): The path to the image..
Image(path: str)
101    def __init__(self, path: str) -> None:
102        self._path = path
is_missing: bool
url: str

The URL to the image.

@staticmethod
def default() -> str:
113    @staticmethod
114    def default() -> str:
115        """Returns the path to the missing Bungie image.
116
117        Note
118        ----
119        This returns the path only, If you want an actual image object use `Image.default_or_else()`
120        """
121        return "/img/misc/missing_icon_d2.png"

Returns the path to the missing Bungie image.

Note

This returns the path only, If you want an actual image object use Image.default_or_else()

@classmethod
def default_or_else(cls, path: str | None = None) -> typing_extensions.Self:
123    @classmethod
124    def default_or_else(cls, path: str | None = None) -> Self:
125        """Return the default image if `path` was `None` otherwise an `Image` object.
126
127        Example
128        -------
129        ```py
130        img = Image.default_or_else(None)
131        print(img.url()) # https://www.bungie.net/img/misc/missing_icon_d2.png
132
133        img = Image.default_or_else("/some_path/image.png")
134        ```
135        """
136        return cls(path or Image.default())

Return the default image if path was None otherwise an Image object.

Example
img = Image.default_or_else(None)
print(img.url()) # https://www.bungie.net/img/misc/missing_icon_d2.png

img = Image.default_or_else("/some_path/image.png")
def create_url(self) -> str:
138    def create_url(self) -> str:
139        """Creates a full URL to the image path.
140
141        Returns
142        -------
143        str
144            The URL to the image.
145        """
146        return f"{url.BASE}/{self._path if self._path else self.default()}"

Creates a full URL to the image path.

Returns
  • str: The URL to the image.
async def save( self, file_name: str, path: pathlib.Path | str, /, mime_type: aiobungie.internal.assets.MimeType | str = <MimeType.JPEG: jpeg>) -> None:
148    async def save(
149        self,
150        file_name: str,
151        path: pathlib.Path | str,
152        /,
153        mime_type: MimeType | str = MimeType.JPEG,
154    ) -> None:
155        """Saves the image to a file.
156
157        Parameters
158        ----------
159        file_name : `str`
160            A name for the file to save the image to.
161        path : `pathlib.Path | str`
162            A path tp save the image to.
163
164        Other Parameters
165        ----------------
166        mime_type : `MimeType | str`
167            MIME type of the image. Defaults to JPEG.
168
169        Raises
170        ------
171        `FileNotFoundError`
172            If the path provided does not exist.
173        `RuntimeError`
174            If the image could not be saved.
175        `PermissionError`
176            If the path provided is not writable or does not have write permissions.
177        """
178        if isinstance(path, pathlib.Path) and not path.exists():
179            raise FileNotFoundError(f"File does not exist: {path!r}")
180
181        if self.is_missing:
182            return
183
184        path = pathlib.Path(path)
185
186        loop = helpers.get_or_make_loop()
187        pool = concurrent.futures.ThreadPoolExecutor()
188
189        try:
190            with pool:
191                await loop.run_in_executor(
192                    pool, _write, path, file_name, mime_type, await self.read()
193                )
194                _LOGGER.info("Saved image to %s", file_name)
195
196        except asyncio.CancelledError:
197            pass
198
199        except Exception as err:
200            raise RuntimeError("Encountered an error while saving image.") from err

Saves the image to a file.

Parameters
  • file_name (str): A name for the file to save the image to.
  • path (pathlib.Path | str): A path tp save the image to.
Other Parameters
  • mime_type (MimeType | str): MIME type of the image. Defaults to JPEG.
Raises
  • FileNotFoundError: If the path provided does not exist.
  • RuntimeError: If the image could not be saved.
  • PermissionError: If the path provided is not writable or does not have write permissions.
async def read(self) -> bytes:
202    async def read(self) -> bytes:
203        """Read this image bytes.
204
205        Returns
206        -------
207        `bytes`
208            The bytes of this image.
209        """
210        client_session = aiohttp.ClientSession()
211
212        try:
213            await client_session.__aenter__()
214            response = await client_session.get(self.create_url())
215
216            if 300 >= response.status >= 200:
217                reader = await response.read()
218
219        except Exception as exc:
220            raise RuntimeError(f"Failed to read image: {exc}") from None
221        finally:
222            await client_session.__aexit__(None, None, None)
223        return reader

Read this image bytes.

Returns
  • bytes: The bytes of this image.
async def iter(self) -> collections.abc.AsyncGenerator[bytes, None]:
225    async def iter(self) -> collections.AsyncGenerator[bytes, None]:
226        """Iterates over the image bytes lazily.
227
228        Example
229        -------
230        import aiobungie
231
232        resource = aiobungie.Image("img/misc/missing_icon_d2.png")
233        async for chunk in resource.iter():
234            print(chunk)
235
236        Returns
237        -------
238        `collections.AsyncGenerator[bytes, None]`
239            An async generator of the image bytes.
240        """
241
242        async for chunk in self:
243            yield chunk

Iterates over the image bytes lazily.

Example

import aiobungie

resource = aiobungie.Image("img/misc/missing_icon_d2.png") async for chunk in resource.iter(): print(chunk)

Returns
  • collections.AsyncGenerator[bytes, None]: An async generator of the image bytes.
@attrs.define(auto_exc=True)
class InternalServerError(aiobungie.HTTPException):
241@attrs.define(auto_exc=True)
242class InternalServerError(HTTPException):
243    """Raised for 5xx internal server errors."""

Raised for 5xx internal server errors.

InternalServerError( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class InternalServerError.

Inherited Members
HTTPException
error_code
http_status
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class ItemBindStatus(builtins.int, aiobungie.Enum):
710@typing.final
711class ItemBindStatus(int, Enum):
712    """An enum for Destiny 2 items bind status."""
713
714    NOT_BOUND = 0
715    BOUND_TO_CHARACTER = 1
716    BOUND_TO_ACCOUNT = 2
717    BOUNT_TO_GUILD = 3

An enum for Destiny 2 items bind status.

NOT_BOUND = <ItemBindStatus.NOT_BOUND: 0>
BOUND_TO_CHARACTER = <ItemBindStatus.BOUND_TO_CHARACTER: 1>
BOUND_TO_ACCOUNT = <ItemBindStatus.BOUND_TO_ACCOUNT: 2>
BOUNT_TO_GUILD = <ItemBindStatus.BOUNT_TO_GUILD: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemLocation(builtins.int, aiobungie.Enum):
720@typing.final
721class ItemLocation(int, Enum):
722    """An enum for Destiny 2 items location."""
723
724    UNKNOWN = 0
725    INVENTORY = 1
726    VAULT = 2
727    VENDOR = 3
728    POSTMASTER = 4

An enum for Destiny 2 items location.

UNKNOWN = <ItemLocation.UNKNOWN: 0>
INVENTORY = <ItemLocation.INVENTORY: 1>
VAULT = <ItemLocation.VAULT: 2>
VENDOR = <ItemLocation.VENDOR: 3>
POSTMASTER = <ItemLocation.POSTMASTER: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemState(aiobungie.Flag):
745@typing.final
746class ItemState(Flag):
747    """An enum for Destiny 2 item states."""
748
749    NONE = 0
750    LOCKED = 1 << 0
751    TRACKED = 1 << 1
752    MASTERWORKED = 1 << 2
753    CRAFTED = 1 << 3
754    """If this bit is set, the item has been 'crafted' by the player."""
755    HIGHLITED_OBJECTIVE = 1 << 4
756    """If this bit is set, the item is a 'highlighted' objective."""

An enum for Destiny 2 item states.

NONE = <ItemState.NONE: 0>
LOCKED = <ItemState.LOCKED: 1>
TRACKED = <ItemState.TRACKED: 2>
MASTERWORKED = <ItemState.MASTERWORKED: 4>
CRAFTED = <ItemState.CRAFTED: 8>

If this bit is set, the item has been 'crafted' by the player.

HIGHLITED_OBJECTIVE = <ItemState.HIGHLITED_OBJECTIVE: 16>

If this bit is set, the item is a 'highlighted' objective.

Inherited Members
Flag
name
value
@typing.final
class ItemSubType(builtins.int, aiobungie.Enum):
577@typing.final
578class ItemSubType(int, Enum):
579    """An enum for Destiny 2 inventory items subtype."""
580
581    NONE = 0
582    AUTORIFLE = 6
583    SHOTGUN = 7
584    MACHINEGUN = 8
585    HANDCANNON = 9
586    ROCKETLAUNCHER = 10
587    FUSIONRIFLE = 11
588    SNIPERRIFLE = 12
589    PULSERIFLE = 13
590    SCOUTRIFLE = 14
591    SIDEARM = 17
592    SWORD = 18
593    MASK = 19
594    SHADER = 20
595    ORNAMENT = 21
596    FUSIONRIFLELINE = 22
597    GRENADELAUNCHER = 23
598    SUBMACHINEGUN = 24
599    TRACERIFLE = 25
600    HELMETARMOR = 26
601    GAUNTLETSARMOR = 27
602    CHESTARMOR = 28
603    LEGARMOR = 29
604    CLASSARMOR = 30
605    BOW = 31
606    DUMMYREPEATABLEBOUNTY = 32

An enum for Destiny 2 inventory items subtype.

NONE = <ItemSubType.NONE: 0>
AUTORIFLE = <ItemSubType.AUTORIFLE: 6>
SHOTGUN = <ItemSubType.SHOTGUN: 7>
MACHINEGUN = <ItemSubType.MACHINEGUN: 8>
HANDCANNON = <ItemSubType.HANDCANNON: 9>
ROCKETLAUNCHER = <ItemSubType.ROCKETLAUNCHER: 10>
FUSIONRIFLE = <ItemSubType.FUSIONRIFLE: 11>
SNIPERRIFLE = <ItemSubType.SNIPERRIFLE: 12>
PULSERIFLE = <ItemSubType.PULSERIFLE: 13>
SCOUTRIFLE = <ItemSubType.SCOUTRIFLE: 14>
SIDEARM = <ItemSubType.SIDEARM: 17>
SWORD = <ItemSubType.SWORD: 18>
MASK = <ItemSubType.MASK: 19>
SHADER = <ItemSubType.SHADER: 20>
ORNAMENT = <ItemSubType.ORNAMENT: 21>
FUSIONRIFLELINE = <ItemSubType.FUSIONRIFLELINE: 22>
GRENADELAUNCHER = <ItemSubType.GRENADELAUNCHER: 23>
SUBMACHINEGUN = <ItemSubType.SUBMACHINEGUN: 24>
TRACERIFLE = <ItemSubType.TRACERIFLE: 25>
HELMETARMOR = <ItemSubType.HELMETARMOR: 26>
GAUNTLETSARMOR = <ItemSubType.GAUNTLETSARMOR: 27>
CHESTARMOR = <ItemSubType.CHESTARMOR: 28>
LEGARMOR = <ItemSubType.LEGARMOR: 29>
CLASSARMOR = <ItemSubType.CLASSARMOR: 30>
BOW = <ItemSubType.BOW: 31>
DUMMYREPEATABLEBOUNTY = <ItemSubType.DUMMYREPEATABLEBOUNTY: 32>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemTier(builtins.int, aiobungie.Enum):
609@typing.final
610class ItemTier(int, Enum):
611    """An enum for a Destiny 2 item tier."""
612
613    NONE = 0
614    BASIC = 3340296461
615    COMMON = 2395677314
616    RARE = 2127292149
617    LEGENDERY = 4008398120
618    EXOTIC = 2759499571

An enum for a Destiny 2 item tier.

NONE = <ItemTier.NONE: 0>
BASIC = <ItemTier.BASIC: 3340296461>
COMMON = <ItemTier.COMMON: 2395677314>
RARE = <ItemTier.RARE: 2127292149>
LEGENDERY = <ItemTier.LEGENDERY: 4008398120>
EXOTIC = <ItemTier.EXOTIC: 2759499571>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemType(builtins.int, aiobungie.Enum):
544@typing.final
545class ItemType(int, Enum):
546    """Enums for Destiny2's item types."""
547
548    NONE = 0
549    CURRENCY = 1
550    ARMOR = 2
551    WEAPON = 3
552    MESSAGE = 7
553    ENGRAM = 8
554    CONSUMABLE = 9
555    EXCHANGEMATERIAL = 10
556    MISSIONREWARD = 11
557    QUESTSTEP = 12
558    QUESTSTEPCOMPLETE = 13
559    EMBLEM = 14
560    QUEST = 15
561    SUBCLASS = 16
562    CLANBANNER = 17
563    AURA = 18
564    MOD = 19
565    DUMMY = 20
566    SHIP = 21
567    VEHICLE = 22
568    EMOTE = 23
569    GHOST = 24
570    PACKAGE = 25
571    BOUNTY = 26
572    WRAPPER = 27
573    SEASONALARTIFACT = 28
574    FINISHER = 29

Enums for Destiny2's item types.

NONE = <ItemType.NONE: 0>
CURRENCY = <ItemType.CURRENCY: 1>
ARMOR = <ItemType.ARMOR: 2>
WEAPON = <ItemType.WEAPON: 3>
MESSAGE = <ItemType.MESSAGE: 7>
ENGRAM = <ItemType.ENGRAM: 8>
CONSUMABLE = <ItemType.CONSUMABLE: 9>
EXCHANGEMATERIAL = <ItemType.EXCHANGEMATERIAL: 10>
MISSIONREWARD = <ItemType.MISSIONREWARD: 11>
QUESTSTEP = <ItemType.QUESTSTEP: 12>
QUESTSTEPCOMPLETE = <ItemType.QUESTSTEPCOMPLETE: 13>
EMBLEM = <ItemType.EMBLEM: 14>
QUEST = <ItemType.QUEST: 15>
SUBCLASS = <ItemType.SUBCLASS: 16>
CLANBANNER = <ItemType.CLANBANNER: 17>
AURA = <ItemType.AURA: 18>
MOD = <ItemType.MOD: 19>
DUMMY = <ItemType.DUMMY: 20>
SHIP = <ItemType.SHIP: 21>
VEHICLE = <ItemType.VEHICLE: 22>
EMOTE = <ItemType.EMOTE: 23>
GHOST = <ItemType.GHOST: 24>
PACKAGE = <ItemType.PACKAGE: 25>
BOUNTY = <ItemType.BOUNTY: 26>
WRAPPER = <ItemType.WRAPPER: 27>
SEASONALARTIFACT = <ItemType.SEASONALARTIFACT: 28>
FINISHER = <ItemType.FINISHER: 29>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Iterator(typing.Generic[~Item], collections.abc.Iterator[~Item]):
 47class Iterator(typing.Generic[Item], collections.Iterator[Item]):
 48    """A Flat, In-Memory iterator for sequenced based data.
 49
 50    Example
 51    -------
 52    ```py
 53    iterator = Iterator([1, 2, 3])
 54
 55    # Map the results.
 56    for item in iterator.map(lambda item: item * 2):
 57        print(item)
 58    # 2
 59    # 4
 60
 61    # Indexing is also supported.
 62    print(iterator[0])
 63    # 1
 64
 65    # Normal iteration.
 66    for item in iterator:
 67        print(item)
 68    # 1
 69    # 2
 70    # 3
 71
 72    # Union two iterators.
 73    iterator2 = Iterator([4, 5, 6])
 74    final = iterator | iterator2
 75    # <Iterator([1, 2, 3, 4, 5, 6])>
 76    ```
 77
 78    Parameters
 79    ----------
 80    items: `collections.Iterable[Item]`
 81        The items to iterate over.
 82    """
 83
 84    __slots__ = ("_items",)
 85
 86    def __init__(self, items: collections.Iterable[Item]) -> None:
 87        self._items = _builtins.iter(items)
 88
 89    @typing.overload
 90    def collect(self) -> list[Item]:
 91        ...
 92
 93    @typing.overload
 94    def collect(self, casting: _B) -> list[_B]:
 95        ...
 96
 97    def collect(self, casting: _B | None = None) -> list[Item] | list[_B]:
 98        """Collects all items in the iterator into a list and cast them into an object if provided.
 99
100        Example
101        -------
102        >>> iterator = Iterator([1, 2, 3])
103        >>> iterator.collect(casting=str)
104        ["1", "2", "3"]
105
106        Parameters
107        ----------
108        casting: `T | None`
109            The type to cast the items to. If `None` is provided, the items will be returned as is.
110
111        Raises
112        ------
113        `StopIteration`
114            If no elements are left in the iterator.
115        """
116        if casting is not None:
117            return typing.cast(list[_B], list(map(casting, self._items)))
118
119        return list(self._items)
120
121    def copied(self) -> Iterator[Item]:
122        """Creates an iterator which `deeply` copies all of its elements.
123
124        Example
125        -------
126        ```py
127        it = Iterator([None, None, None])
128        copied_iter = it.copied()
129        assert it.collect() == copied.collect()
130        ```
131        """
132        return Iterator(_copy.deepcopy(self._items))
133
134    def next(self) -> Item:
135        """Returns the next item in the iterator.
136
137        Example
138        -------
139        ```py
140        iterator = Iterator(["1", "2", "3"])
141        item = iterator.next()
142        assert item == "1"
143        item = iterator.next()
144        assert item == "2"
145        ```
146
147        Raises
148        ------
149        `StopIteration`
150            If no elements are left in the iterator.
151        """
152        try:
153            return self.__next__()
154        except StopIteration:
155            self._ok()
156
157    def map(
158        self, predicate: collections.Callable[[Item], OtherItem]
159    ) -> Iterator[OtherItem]:
160        """Maps each item in the iterator to its predicated value.
161
162        Example
163        -------
164        ```py
165        iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
166        print(iterator)
167        # <Iterator([1, 2, 3])>
168        ```
169
170        Parameters
171        ----------
172        predicate: `collections.Callable[[Item], OtherItem]`
173            The function to map each item in the iterator to its predicated value.
174
175        Returns
176        -------
177        `Iterator[OtherItem]`
178            The mapped iterator.
179
180        Raises
181        ------
182        `StopIteration`
183            If no elements are left in the iterator.
184        """
185        return Iterator(map(predicate, self._items))
186
187    def take(self, n: int) -> Iterator[Item]:
188        """Take the first number of items until the number of items are yielded or
189        the end of the iterator is reached.
190
191        Example
192        -------
193        ```py
194        iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
195        print(iterator.take(2))
196        # <Iterator([GameMode.RAID, GameMode.STRIKE])>
197        ```
198
199        Parameters
200        ----------
201        n: `int`
202            The number of items to take.
203
204        Raises
205        ------
206        `StopIteration`
207            If no elements are left in the iterator.
208        """
209        return Iterator(itertools.islice(self._items, n))
210
211    def take_while(
212        self, predicate: collections.Callable[[Item], bool]
213    ) -> Iterator[Item]:
214        """Yields items from the iterator while predicate returns `True`.
215
216        Example
217        -------
218        ```py
219        iterator = Iterator([STEAM, XBOX, STADIA])
220        print(iterator.take_while(lambda platform: platform is not XBOX))
221        # <Iterator([STEAM])>
222        ```
223
224        Parameters
225        ----------
226        predicate: `collections.Callable[[Item], bool]`
227            The function to predicate each item in the iterator.
228
229        Raises
230        ------
231        `StopIteration`
232            If no elements are left in the iterator.
233        """
234        return Iterator(itertools.takewhile(predicate, self._items))
235
236    def drop_while(
237        self, predicate: collections.Callable[[Item], bool]
238    ) -> Iterator[Item]:
239        """Yields items from the iterator while predicate returns `False`.
240
241        Example
242        -------
243        ```py
244        iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
245        print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
246        # <Iterator([DestinyMembership(name="Bob")])>
247        ```
248
249        Parameters
250        ----------
251        predicate: `collections.Callable[[Item], bool]`
252            The function to predicate each item in the iterator.
253
254        Raises
255        ------
256        `StopIteration`
257            If no elements are left in the iterator.
258        """
259        return Iterator(itertools.dropwhile(predicate, self._items))
260
261    def filter(self, predicate: collections.Callable[[Item], bool]) -> Iterator[Item]:
262        """Filters the iterator to only yield items that match the predicate.
263
264        Example
265        -------
266        ```py
267        names = Iterator(["Jim", "Bob", "Mike", "Jess"])
268        print(names.filter(lambda n: n != "Jim"))
269        # <Iterator(["Bob", "Mike", "Jess"])>
270        ```
271        """
272        return Iterator(filter(predicate, self._items))
273
274    def skip(self, n: int) -> Iterator[Item]:
275        """Skips the first number of items in the iterator.
276
277        Example
278        -------
279        ```py
280        iterator = Iterator([STEAM, XBOX, STADIA])
281        print(iterator.skip(1))
282        # <Iterator([XBOX, STADIA])>
283        ```
284        """
285        return Iterator(itertools.islice(self._items, n, None))
286
287    def zip(self, other: Iterator[OtherItem]) -> Iterator[tuple[Item, OtherItem]]:
288        """Zips the iterator with another iterable.
289
290        Example
291        -------
292        ```py
293        iterator = Iterator([1, 3, 5])
294        other = Iterator([2, 4, 6])
295        for item, other_item in iterator.zip(other):
296            print(item, other_item)
297        # <Iterator([(1, 2), (3, 4), (5, 6)])>
298        ```
299
300        Parameters
301        ----------
302        other: `Iterator[OtherItem]`
303            The iterable to zip with.
304
305        Raises
306        ------
307        `StopIteration`
308            If no elements are left in the iterator.
309        """
310        return Iterator(zip(self._items, other))
311
312    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
313        """`True` if all items in the iterator match the predicate.
314
315        Example
316        -------
317        ```py
318        iterator = Iterator([1, 2, 3])
319        while iterator.all(lambda item: isinstance(item, int)):
320            print("Still all integers")
321            continue
322        # Still all integers
323        ```
324
325        Parameters
326        ----------
327        predicate: `collections.Callable[[Item], bool]`
328            The function to test each item in the iterator.
329
330        Raises
331        ------
332        `StopIteration`
333            If no elements are left in the iterator.
334        """
335        return all(predicate(item) for item in self)
336
337    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
338        """`True` if any items in the iterator match the predicate.
339
340        Example
341        -------
342        ```py
343        iterator = Iterator([1, 2, 3])
344        if iterator.any(lambda item: isinstance(item, int)):
345            print("At least one item is an int.")
346        # At least one item is an int.
347        ```
348
349        Parameters
350        ----------
351        predicate: `collections.Callable[[Item], bool]`
352            The function to test each item in the iterator.
353
354        Raises
355        ------
356        `StopIteration`
357            If no elements are left in the iterator.
358        """
359        return any(predicate(item) for item in self)
360
361    def sort(
362        self,
363        *,
364        key: collections.Callable[[Item], typeshed.SupportsRichComparison],
365        reverse: bool = False,
366    ) -> Iterator[Item]:
367        """Sorts the iterator.
368
369        Example
370        -------
371        ```py
372        iterator = Iterator([3, 1, 6, 7])
373        print(iterator.sort(key=lambda item: item))
374        # <Iterator([1, 3, 6, 7])>
375        ```
376
377        Parameters
378        ----------
379        key: `collections.Callable[[Item], Any]`
380            The function to sort by.
381        reverse: `bool`
382            Whether to reverse the sort.
383
384        Raises
385        ------
386        `StopIteration`
387            If no elements are left in the iterator.
388        """
389        return Iterator(sorted(self._items, key=key, reverse=reverse))
390
391    def first(self) -> Item:
392        """Returns the first item in the iterator.
393
394        Example
395        -------
396        ```py
397        iterator = Iterator([3, 1, 6, 7])
398        print(iterator.first())
399        3
400        ```
401
402        Raises
403        ------
404        `StopIteration`
405            If no elements are left in the iterator.
406        """
407        return self.take(1).next()
408
409    def last(self) -> Item:
410        """Returns the last item in the iterator.
411
412        Example
413        ------
414        ```py
415        it = Iterator((1, 2, 3))
416        assert it.first() == 1 and it.last() == 3
417        ```
418        """
419        return self.reversed().first()
420
421    def reversed(self) -> Iterator[Item]:
422        """Returns a new iterator that yields the items in the iterator in reverse order.
423
424        Example
425        -------
426        ```py
427        iterator = Iterator([3, 1, 6, 7])
428        print(iterator.reversed())
429        # <Iterator([7, 6, 1, 3])>
430        ```
431
432        Raises
433        ------
434        `StopIteration`
435            If no elements are left in the iterator.
436        """
437        return Iterator(reversed(self.collect()))
438
439    def count(self) -> int:
440        """Returns the number of items in the iterator.
441
442        Example
443        -------
444        ```py
445        iterator = Iterator([3, 1, 6, 7])
446        print(iterator.count())
447        4
448        ```
449        """
450        count = 0
451        for _ in self:
452            count += 1
453
454        return count
455
456    def union(self, other: Iterator[Item]) -> Iterator[Item]:
457        """Returns a new iterator that yields all items from both iterators.
458
459        Example
460        -------
461        ```py
462        iterator = Iterator([1, 2, 3])
463        other = Iterator([4, 5, 6])
464        print(iterator.union(other))
465        # <Iterator([1, 2, 3, 4, 5, 6])>
466        ```
467
468        Parameters
469        ----------
470        other: `Iterator[Item]`
471            The iterable to union with.
472
473        Raises
474        ------
475        `StopIteration`
476            If no elements are left in the iterator.
477        """
478        return Iterator(itertools.chain(self._items, other))
479
480    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
481        """Calls the function on each item in the iterator.
482
483        Example
484        -------
485        ```py
486        iterator = Iterator([1, 2, 3])
487        iterator.for_each(lambda item: print(item))
488        # 1
489        # 2
490        # 3
491        ```
492
493        Parameters
494        ----------
495        func: `typeshed.Callable[[Item], None]`
496            The function to call on each item in the iterator.
497        """
498        for item in self:
499            func(item)
500
501    async def async_for_each(
502        self,
503        func: collections.Callable[[Item], collections.Coroutine[None, None, None]],
504    ) -> None:
505        """Calls the async function on each item in the iterator concurrently.
506
507        Example
508        -------
509        ```py
510        async def signup(username: str) -> None:
511            async with aiohttp.request('POST', '...') as r:
512                # Actual logic.
513                ...
514
515        async def main():
516            users = aiobungie.into_iter(["user_danny", "user_jojo"])
517            await users.async_for_each(lambda username: signup(username))
518        ```
519
520        Parameters
521        ----------
522        func: `collections.Callable[[Item], collections.Coroutine[None, None, None]]`
523            The async function to call on each item in the iterator.
524        """
525        await _helpers.awaits(*(func(item) for item in self))
526
527    def enumerate(self, *, start: int = 0) -> Iterator[tuple[int, Item]]:
528        """Returns a new iterator that yields tuples of the index and item.
529
530        Example
531        -------
532        ```py
533        iterator = Iterator([1, 2, 3])
534        for index, item in iterator.enumerate():
535            print(index, item)
536        # 0 1
537        # 1 2
538        # 2 3
539        ```
540
541        Raises
542        ------
543        `StopIteration`
544            If no elements are left in the iterator.
545        """
546        return Iterator(enumerate(self._items, start=start))
547
548    def _ok(self) -> typing.NoReturn:
549        raise StopIteration("No more items in the iterator.") from None
550
551    def __getitem__(self, index: int) -> Item:
552        try:
553            return self.skip(index).first()
554        except IndexError:
555            self._ok()
556
557    def __or__(self, other: Iterator[Item]) -> Iterator[Item]:
558        return self.union(other)
559
560    # This is a never.
561    def __setitem__(self) -> typing.NoReturn:
562        raise TypeError(
563            f"{type(self).__name__} doesn't support item assignment."
564        ) from None
565
566    def __repr__(self) -> str:
567        return f'<{self.__class__.__name__}({", ".join([str(item) for item in self])})>'
568
569    def __len__(self) -> int:
570        return self.count()
571
572    def __iter__(self) -> Iterator[Item]:
573        return self
574
575    def __next__(self) -> Item:
576        try:
577            item = next(self._items)
578        except StopIteration:
579            self._ok()
580
581        return item

A Flat, In-Memory iterator for sequenced based data.

Example
iterator = Iterator([1, 2, 3])

# Map the results.
for item in iterator.map(lambda item: item * 2):
    print(item)
# 2
# 4

# Indexing is also supported.
print(iterator[0])
# 1

# Normal iteration.
for item in iterator:
    print(item)
# 1
# 2
# 3

# Union two iterators.
iterator2 = Iterator([4, 5, 6])
final = iterator | iterator2
# <Iterator([1, 2, 3, 4, 5, 6])>
Parameters
  • items (collections.Iterable[Item]): The items to iterate over.
Iterator(items: collections.abc.Iterable[~Item])
86    def __init__(self, items: collections.Iterable[Item]) -> None:
87        self._items = _builtins.iter(items)
def collect(self, casting: '_B | None' = None) -> 'list[Item] | list[_B]':
 97    def collect(self, casting: _B | None = None) -> list[Item] | list[_B]:
 98        """Collects all items in the iterator into a list and cast them into an object if provided.
 99
100        Example
101        -------
102        >>> iterator = Iterator([1, 2, 3])
103        >>> iterator.collect(casting=str)
104        ["1", "2", "3"]
105
106        Parameters
107        ----------
108        casting: `T | None`
109            The type to cast the items to. If `None` is provided, the items will be returned as is.
110
111        Raises
112        ------
113        `StopIteration`
114            If no elements are left in the iterator.
115        """
116        if casting is not None:
117            return typing.cast(list[_B], list(map(casting, self._items)))
118
119        return list(self._items)

Collects all items in the iterator into a list and cast them into an object if provided.

Example
>>> iterator = Iterator([1, 2, 3])
>>> iterator.collect(casting=str)
["1", "2", "3"]
Parameters
  • casting (T | None): The type to cast the items to. If None is provided, the items will be returned as is.
Raises
  • StopIteration: If no elements are left in the iterator.
def copied(self) -> Iterator[~Item]:
121    def copied(self) -> Iterator[Item]:
122        """Creates an iterator which `deeply` copies all of its elements.
123
124        Example
125        -------
126        ```py
127        it = Iterator([None, None, None])
128        copied_iter = it.copied()
129        assert it.collect() == copied.collect()
130        ```
131        """
132        return Iterator(_copy.deepcopy(self._items))

Creates an iterator which deeply copies all of its elements.

Example
it = Iterator([None, None, None])
copied_iter = it.copied()
assert it.collect() == copied.collect()
def next(self) -> ~Item:
134    def next(self) -> Item:
135        """Returns the next item in the iterator.
136
137        Example
138        -------
139        ```py
140        iterator = Iterator(["1", "2", "3"])
141        item = iterator.next()
142        assert item == "1"
143        item = iterator.next()
144        assert item == "2"
145        ```
146
147        Raises
148        ------
149        `StopIteration`
150            If no elements are left in the iterator.
151        """
152        try:
153            return self.__next__()
154        except StopIteration:
155            self._ok()

Returns the next item in the iterator.

Example
iterator = Iterator(["1", "2", "3"])
item = iterator.next()
assert item == "1"
item = iterator.next()
assert item == "2"
Raises
  • StopIteration: If no elements are left in the iterator.
def map( self, predicate: 'collections.Callable[[Item], OtherItem]') -> 'Iterator[OtherItem]':
157    def map(
158        self, predicate: collections.Callable[[Item], OtherItem]
159    ) -> Iterator[OtherItem]:
160        """Maps each item in the iterator to its predicated value.
161
162        Example
163        -------
164        ```py
165        iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
166        print(iterator)
167        # <Iterator([1, 2, 3])>
168        ```
169
170        Parameters
171        ----------
172        predicate: `collections.Callable[[Item], OtherItem]`
173            The function to map each item in the iterator to its predicated value.
174
175        Returns
176        -------
177        `Iterator[OtherItem]`
178            The mapped iterator.
179
180        Raises
181        ------
182        `StopIteration`
183            If no elements are left in the iterator.
184        """
185        return Iterator(map(predicate, self._items))

Maps each item in the iterator to its predicated value.

Example
iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
print(iterator)
# <Iterator([1, 2, 3])>
Parameters
  • predicate (collections.Callable[[Item], OtherItem]): The function to map each item in the iterator to its predicated value.
Returns
  • Iterator[OtherItem]: The mapped iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def take(self, n: int) -> Iterator[~Item]:
187    def take(self, n: int) -> Iterator[Item]:
188        """Take the first number of items until the number of items are yielded or
189        the end of the iterator is reached.
190
191        Example
192        -------
193        ```py
194        iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
195        print(iterator.take(2))
196        # <Iterator([GameMode.RAID, GameMode.STRIKE])>
197        ```
198
199        Parameters
200        ----------
201        n: `int`
202            The number of items to take.
203
204        Raises
205        ------
206        `StopIteration`
207            If no elements are left in the iterator.
208        """
209        return Iterator(itertools.islice(self._items, n))

Take the first number of items until the number of items are yielded or the end of the iterator is reached.

Example
iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
print(iterator.take(2))
# <Iterator([GameMode.RAID, GameMode.STRIKE])>
Parameters
  • n (int): The number of items to take.
Raises
  • StopIteration: If no elements are left in the iterator.
def take_while( self, predicate: collections.abc.Callable[[~Item], bool]) -> Iterator[~Item]:
211    def take_while(
212        self, predicate: collections.Callable[[Item], bool]
213    ) -> Iterator[Item]:
214        """Yields items from the iterator while predicate returns `True`.
215
216        Example
217        -------
218        ```py
219        iterator = Iterator([STEAM, XBOX, STADIA])
220        print(iterator.take_while(lambda platform: platform is not XBOX))
221        # <Iterator([STEAM])>
222        ```
223
224        Parameters
225        ----------
226        predicate: `collections.Callable[[Item], bool]`
227            The function to predicate each item in the iterator.
228
229        Raises
230        ------
231        `StopIteration`
232            If no elements are left in the iterator.
233        """
234        return Iterator(itertools.takewhile(predicate, self._items))

Yields items from the iterator while predicate returns True.

Example
iterator = Iterator([STEAM, XBOX, STADIA])
print(iterator.take_while(lambda platform: platform is not XBOX))
# <Iterator([STEAM])>
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def drop_while( self, predicate: collections.abc.Callable[[~Item], bool]) -> Iterator[~Item]:
236    def drop_while(
237        self, predicate: collections.Callable[[Item], bool]
238    ) -> Iterator[Item]:
239        """Yields items from the iterator while predicate returns `False`.
240
241        Example
242        -------
243        ```py
244        iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
245        print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
246        # <Iterator([DestinyMembership(name="Bob")])>
247        ```
248
249        Parameters
250        ----------
251        predicate: `collections.Callable[[Item], bool]`
252            The function to predicate each item in the iterator.
253
254        Raises
255        ------
256        `StopIteration`
257            If no elements are left in the iterator.
258        """
259        return Iterator(itertools.dropwhile(predicate, self._items))

Yields items from the iterator while predicate returns False.

Example
iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
# <Iterator([DestinyMembership(name="Bob")])>
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def filter( self, predicate: collections.abc.Callable[[~Item], bool]) -> Iterator[~Item]:
261    def filter(self, predicate: collections.Callable[[Item], bool]) -> Iterator[Item]:
262        """Filters the iterator to only yield items that match the predicate.
263
264        Example
265        -------
266        ```py
267        names = Iterator(["Jim", "Bob", "Mike", "Jess"])
268        print(names.filter(lambda n: n != "Jim"))
269        # <Iterator(["Bob", "Mike", "Jess"])>
270        ```
271        """
272        return Iterator(filter(predicate, self._items))

Filters the iterator to only yield items that match the predicate.

Example
names = Iterator(["Jim", "Bob", "Mike", "Jess"])
print(names.filter(lambda n: n != "Jim"))
# <Iterator(["Bob", "Mike", "Jess"])>
def skip(self, n: int) -> Iterator[~Item]:
274    def skip(self, n: int) -> Iterator[Item]:
275        """Skips the first number of items in the iterator.
276
277        Example
278        -------
279        ```py
280        iterator = Iterator([STEAM, XBOX, STADIA])
281        print(iterator.skip(1))
282        # <Iterator([XBOX, STADIA])>
283        ```
284        """
285        return Iterator(itertools.islice(self._items, n, None))

Skips the first number of items in the iterator.

Example
iterator = Iterator([STEAM, XBOX, STADIA])
print(iterator.skip(1))
# <Iterator([XBOX, STADIA])>
def zip(self, other: 'Iterator[OtherItem]') -> 'Iterator[tuple[Item, OtherItem]]':
287    def zip(self, other: Iterator[OtherItem]) -> Iterator[tuple[Item, OtherItem]]:
288        """Zips the iterator with another iterable.
289
290        Example
291        -------
292        ```py
293        iterator = Iterator([1, 3, 5])
294        other = Iterator([2, 4, 6])
295        for item, other_item in iterator.zip(other):
296            print(item, other_item)
297        # <Iterator([(1, 2), (3, 4), (5, 6)])>
298        ```
299
300        Parameters
301        ----------
302        other: `Iterator[OtherItem]`
303            The iterable to zip with.
304
305        Raises
306        ------
307        `StopIteration`
308            If no elements are left in the iterator.
309        """
310        return Iterator(zip(self._items, other))

Zips the iterator with another iterable.

Example
iterator = Iterator([1, 3, 5])
other = Iterator([2, 4, 6])
for item, other_item in iterator.zip(other):
    print(item, other_item)
# <Iterator([(1, 2), (3, 4), (5, 6)])>
Parameters
  • other (Iterator[OtherItem]): The iterable to zip with.
Raises
  • StopIteration: If no elements are left in the iterator.
def all(self, predicate: collections.abc.Callable[[~Item], bool]) -> bool:
312    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
313        """`True` if all items in the iterator match the predicate.
314
315        Example
316        -------
317        ```py
318        iterator = Iterator([1, 2, 3])
319        while iterator.all(lambda item: isinstance(item, int)):
320            print("Still all integers")
321            continue
322        # Still all integers
323        ```
324
325        Parameters
326        ----------
327        predicate: `collections.Callable[[Item], bool]`
328            The function to test each item in the iterator.
329
330        Raises
331        ------
332        `StopIteration`
333            If no elements are left in the iterator.
334        """
335        return all(predicate(item) for item in self)

True if all items in the iterator match the predicate.

Example
iterator = Iterator([1, 2, 3])
while iterator.all(lambda item: isinstance(item, int)):
    print("Still all integers")
    continue
# Still all integers
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def any(self, predicate: collections.abc.Callable[[~Item], bool]) -> bool:
337    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
338        """`True` if any items in the iterator match the predicate.
339
340        Example
341        -------
342        ```py
343        iterator = Iterator([1, 2, 3])
344        if iterator.any(lambda item: isinstance(item, int)):
345            print("At least one item is an int.")
346        # At least one item is an int.
347        ```
348
349        Parameters
350        ----------
351        predicate: `collections.Callable[[Item], bool]`
352            The function to test each item in the iterator.
353
354        Raises
355        ------
356        `StopIteration`
357            If no elements are left in the iterator.
358        """
359        return any(predicate(item) for item in self)

True if any items in the iterator match the predicate.

Example
iterator = Iterator([1, 2, 3])
if iterator.any(lambda item: isinstance(item, int)):
    print("At least one item is an int.")
# At least one item is an int.
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def sort( self, *, key: 'collections.Callable[[Item], typeshed.SupportsRichComparison]', reverse: bool = False) -> Iterator[~Item]:
361    def sort(
362        self,
363        *,
364        key: collections.Callable[[Item], typeshed.SupportsRichComparison],
365        reverse: bool = False,
366    ) -> Iterator[Item]:
367        """Sorts the iterator.
368
369        Example
370        -------
371        ```py
372        iterator = Iterator([3, 1, 6, 7])
373        print(iterator.sort(key=lambda item: item))
374        # <Iterator([1, 3, 6, 7])>
375        ```
376
377        Parameters
378        ----------
379        key: `collections.Callable[[Item], Any]`
380            The function to sort by.
381        reverse: `bool`
382            Whether to reverse the sort.
383
384        Raises
385        ------
386        `StopIteration`
387            If no elements are left in the iterator.
388        """
389        return Iterator(sorted(self._items, key=key, reverse=reverse))

Sorts the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.sort(key=lambda item: item))
# <Iterator([1, 3, 6, 7])>
Parameters
  • key (collections.Callable[[Item], Any]): The function to sort by.
  • reverse (bool): Whether to reverse the sort.
Raises
  • StopIteration: If no elements are left in the iterator.
def first(self) -> ~Item:
391    def first(self) -> Item:
392        """Returns the first item in the iterator.
393
394        Example
395        -------
396        ```py
397        iterator = Iterator([3, 1, 6, 7])
398        print(iterator.first())
399        3
400        ```
401
402        Raises
403        ------
404        `StopIteration`
405            If no elements are left in the iterator.
406        """
407        return self.take(1).next()

Returns the first item in the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.first())
3
Raises
  • StopIteration: If no elements are left in the iterator.
def last(self) -> ~Item:
409    def last(self) -> Item:
410        """Returns the last item in the iterator.
411
412        Example
413        ------
414        ```py
415        it = Iterator((1, 2, 3))
416        assert it.first() == 1 and it.last() == 3
417        ```
418        """
419        return self.reversed().first()

Returns the last item in the iterator.

Example
it = Iterator((1, 2, 3))
assert it.first() == 1 and it.last() == 3
def reversed(self) -> Iterator[~Item]:
421    def reversed(self) -> Iterator[Item]:
422        """Returns a new iterator that yields the items in the iterator in reverse order.
423
424        Example
425        -------
426        ```py
427        iterator = Iterator([3, 1, 6, 7])
428        print(iterator.reversed())
429        # <Iterator([7, 6, 1, 3])>
430        ```
431
432        Raises
433        ------
434        `StopIteration`
435            If no elements are left in the iterator.
436        """
437        return Iterator(reversed(self.collect()))

Returns a new iterator that yields the items in the iterator in reverse order.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.reversed())
# <Iterator([7, 6, 1, 3])>
Raises
  • StopIteration: If no elements are left in the iterator.
def count(self) -> int:
439    def count(self) -> int:
440        """Returns the number of items in the iterator.
441
442        Example
443        -------
444        ```py
445        iterator = Iterator([3, 1, 6, 7])
446        print(iterator.count())
447        4
448        ```
449        """
450        count = 0
451        for _ in self:
452            count += 1
453
454        return count

Returns the number of items in the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.count())
4
def union( self, other: Iterator[~Item]) -> Iterator[~Item]:
456    def union(self, other: Iterator[Item]) -> Iterator[Item]:
457        """Returns a new iterator that yields all items from both iterators.
458
459        Example
460        -------
461        ```py
462        iterator = Iterator([1, 2, 3])
463        other = Iterator([4, 5, 6])
464        print(iterator.union(other))
465        # <Iterator([1, 2, 3, 4, 5, 6])>
466        ```
467
468        Parameters
469        ----------
470        other: `Iterator[Item]`
471            The iterable to union with.
472
473        Raises
474        ------
475        `StopIteration`
476            If no elements are left in the iterator.
477        """
478        return Iterator(itertools.chain(self._items, other))

Returns a new iterator that yields all items from both iterators.

Example
iterator = Iterator([1, 2, 3])
other = Iterator([4, 5, 6])
print(iterator.union(other))
# <Iterator([1, 2, 3, 4, 5, 6])>
Parameters
  • other (Iterator[Item]): The iterable to union with.
Raises
  • StopIteration: If no elements are left in the iterator.
def for_each(self, func: collections.abc.Callable[[~Item], typing.Any]) -> None:
480    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
481        """Calls the function on each item in the iterator.
482
483        Example
484        -------
485        ```py
486        iterator = Iterator([1, 2, 3])
487        iterator.for_each(lambda item: print(item))
488        # 1
489        # 2
490        # 3
491        ```
492
493        Parameters
494        ----------
495        func: `typeshed.Callable[[Item], None]`
496            The function to call on each item in the iterator.
497        """
498        for item in self:
499            func(item)

Calls the function on each item in the iterator.

Example
iterator = Iterator([1, 2, 3])
iterator.for_each(lambda item: print(item))
# 1
# 2
# 3
Parameters
  • func (typeshed.Callable[[Item], None]): The function to call on each item in the iterator.
async def async_for_each( self, func: collections.abc.Callable[[~Item], collections.abc.Coroutine[None, None, None]]) -> None:
501    async def async_for_each(
502        self,
503        func: collections.Callable[[Item], collections.Coroutine[None, None, None]],
504    ) -> None:
505        """Calls the async function on each item in the iterator concurrently.
506
507        Example
508        -------
509        ```py
510        async def signup(username: str) -> None:
511            async with aiohttp.request('POST', '...') as r:
512                # Actual logic.
513                ...
514
515        async def main():
516            users = aiobungie.into_iter(["user_danny", "user_jojo"])
517            await users.async_for_each(lambda username: signup(username))
518        ```
519
520        Parameters
521        ----------
522        func: `collections.Callable[[Item], collections.Coroutine[None, None, None]]`
523            The async function to call on each item in the iterator.
524        """
525        await _helpers.awaits(*(func(item) for item in self))

Calls the async function on each item in the iterator concurrently.

Example
async def signup(username: str) -> None:
    async with aiohttp.request('POST', '...') as r:
        # Actual logic.
        ...

async def main():
    users = aiobungie.into_iter(["user_danny", "user_jojo"])
    await users.async_for_each(lambda username: signup(username))
Parameters
  • func (collections.Callable[[Item], collections.Coroutine[None, None, None]]): The async function to call on each item in the iterator.
def enumerate( self, *, start: int = 0) -> Iterator[tuple[int, ~Item]]:
527    def enumerate(self, *, start: int = 0) -> Iterator[tuple[int, Item]]:
528        """Returns a new iterator that yields tuples of the index and item.
529
530        Example
531        -------
532        ```py
533        iterator = Iterator([1, 2, 3])
534        for index, item in iterator.enumerate():
535            print(index, item)
536        # 0 1
537        # 1 2
538        # 2 3
539        ```
540
541        Raises
542        ------
543        `StopIteration`
544            If no elements are left in the iterator.
545        """
546        return Iterator(enumerate(self._items, start=start))

Returns a new iterator that yields tuples of the index and item.

Example
iterator = Iterator([1, 2, 3])
for index, item in iterator.enumerate():
    print(index, item)
# 0 1
# 1 2
# 2 3
Raises
  • StopIteration: If no elements are left in the iterator.
@typing.final
class MembershipOption(builtins.int, aiobungie.Enum):
701@typing.final
702class MembershipOption(int, Enum):
703    """A enum for GroupV2 membership options."""
704
705    REVIEWD = 0
706    OPEN = 1
707    CLOSED = 2

A enum for GroupV2 membership options.

REVIEWD = <MembershipOption.REVIEWD: 0>
OPEN = <MembershipOption.OPEN: 1>
CLOSED = <MembershipOption.CLOSED: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class MembershipType(builtins.int, aiobungie.Enum):
449@typing.final
450class MembershipType(int, Enum):
451    """An Enum for Bungie membership types."""
452
453    NONE = 0
454    XBOX = 1
455    PSN = 2
456    STEAM = 3
457    BLIZZARD = 4
458    STADIA = 5
459    EPIC_GAMES_STORE = 6
460    DEMON = 10
461    BUNGIE = 254
462    ALL = -1

An Enum for Bungie membership types.

NONE = <MembershipType.NONE: 0>
XBOX = <MembershipType.XBOX: 1>
PSN = <MembershipType.PSN: 2>
STEAM = <MembershipType.STEAM: 3>
BLIZZARD = <MembershipType.BLIZZARD: 4>
STADIA = <MembershipType.STADIA: 5>
EPIC_GAMES_STORE = <MembershipType.EPIC_GAMES_STORE: 6>
DEMON = <MembershipType.DEMON: 10>
BUNGIE = <MembershipType.BUNGIE: 254>
ALL = <MembershipType.ALL: -1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class MembershipTypeError(aiobungie.BadRequest):
182@attrs.define(auto_exc=True)
183class MembershipTypeError(BadRequest):
184    """A bad request error raised when passing wrong membership to the request.
185
186    Those fields are useful since it returns the correct membership and id which can be used
187    to make the request again with those fields.
188
189    Example
190    -------
191    ```py
192    try:
193        profile = await client.fetch_profile(
194            member_id=1,
195            type=aiobungie.MembershipType.STADIA,
196            components=[]
197        )
198
199    # Membership type is wrong!
200    except aiobungie.MembershipTypeError as err:
201        correct_membersip = err.into_membership()
202        profile_id = err.membership_id
203
204        # Recall the method.
205        profile = await client.fetch_profile(
206            member_id=profile_id,
207            type=correct_membership,
208            components=[]
209        )
210    ```
211    """
212
213    membership_type: str
214    """The errored membership type passed to the request."""
215
216    membership_id: int
217    """The errored user's membership id."""
218
219    required_membership: str
220    """The required correct membership for errored user."""
221
222    def into_membership(self, value: str | None = None) -> enums.MembershipType:
223        """Turn the required membership from `str` into `aiobungie.Membership` type.
224
225        If value parameter is not provided it will fall back to the required membership.
226        """
227        if value is None:
228            return _determine_membership(self.required_membership)
229        return _determine_membership(value)
230
231    def __str__(self) -> str:
232        return (
233            f"Expected membership: {self.into_membership().name.replace('_', '').title()}, "
234            f"But got {self.into_membership(self.membership_type)} for id {self.membership_id}"
235        )
236
237    def __int__(self) -> int:
238        return int(self.membership_id)

A bad request error raised when passing wrong membership to the request.

Those fields are useful since it returns the correct membership and id which can be used to make the request again with those fields.

Example
try:
    profile = await client.fetch_profile(
        member_id=1,
        type=aiobungie.MembershipType.STADIA,
        components=[]
    )

# Membership type is wrong!
except aiobungie.MembershipTypeError as err:
    correct_membersip = err.into_membership()
    profile_id = err.membership_id

    # Recall the method.
    profile = await client.fetch_profile(
        member_id=profile_id,
        type=correct_membership,
        components=[]
    )
MembershipTypeError( message: str, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], membership_type: str, membership_id: int, required_membership: str)
 2def __init__(self, message, url, body, headers, membership_type, membership_id, required_membership):
 3    self.message = message
 4    self.url = url
 5    self.body = body
 6    self.headers = headers
 7    self.http_status = attr_dict['http_status'].default
 8    self.membership_type = membership_type
 9    self.membership_id = membership_id
10    self.required_membership = required_membership
11    BaseException.__init__(self, self.message,self.url,self.body,self.headers,self.membership_type,self.membership_id,self.required_membership)

Method generated by attrs for class MembershipTypeError.

membership_type: str

The errored membership type passed to the request.

membership_id: int

The errored user's membership id.

required_membership: str

The required correct membership for errored user.

def into_membership( self, value: str | None = None) -> MembershipType:
222    def into_membership(self, value: str | None = None) -> enums.MembershipType:
223        """Turn the required membership from `str` into `aiobungie.Membership` type.
224
225        If value parameter is not provided it will fall back to the required membership.
226        """
227        if value is None:
228            return _determine_membership(self.required_membership)
229        return _determine_membership(value)

Turn the required membership from str into aiobungie.Membership type.

If value parameter is not provided it will fall back to the required membership.

Inherited Members
BadRequest
url
body
headers
http_status
HTTPError
message
builtins.BaseException
with_traceback
args
@typing.final
class MilestoneType(builtins.int, aiobungie.Enum):
494@typing.final
495class MilestoneType(int, Enum):
496    """An Enum for Destiny 2 milestone types."""
497
498    UNKNOWN = 0
499    TUTORIAL = 1
500    ONETIME = 2
501    WEEKLY = 3
502    DAILY = 4
503    SPECIAL = 5

An Enum for Destiny 2 milestone types.

UNKNOWN = <MilestoneType.UNKNOWN: 0>
TUTORIAL = <MilestoneType.TUTORIAL: 1>
ONETIME = <MilestoneType.ONETIME: 2>
WEEKLY = <MilestoneType.WEEKLY: 3>
DAILY = <MilestoneType.DAILY: 4>
SPECIAL = <MilestoneType.SPECIAL: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class NotFound(aiobungie.HTTPException):
146@attrs.define(auto_exc=True)
147class NotFound(HTTPException):
148    """Raised when an unknown resource was not found."""
149
150    http_status: http.HTTPStatus = attrs.field(
151        default=http.HTTPStatus.NOT_FOUND, init=False
152    )

Raised when an unknown resource was not found.

NotFound( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class NotFound.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class ObjectiveUIStyle(builtins.int, aiobungie.Enum):
 93@typing.final
 94class ObjectiveUIStyle(int, enums.Enum):
 95    NONE = 0
 96    HIGHLIGHTED = 1
 97    CRAFTING_WEAPON_LEVEL = 2
 98    CRAFTING_WEAPON_LEVEL_PROGRESS = 3
 99    CRAFTING_WEAPON_TIMESTAMP = 4
100    CRAFTING_MEMENTOS = 5
101    CRAFTING_MEMENTO_TITLE = 6

An enumeration.

NONE = <ObjectiveUIStyle.NONE: 0>
HIGHLIGHTED = <ObjectiveUIStyle.HIGHLIGHTED: 1>
CRAFTING_WEAPON_LEVEL = <ObjectiveUIStyle.CRAFTING_WEAPON_LEVEL: 2>
CRAFTING_WEAPON_LEVEL_PROGRESS = <ObjectiveUIStyle.CRAFTING_WEAPON_LEVEL_PROGRESS: 3>
CRAFTING_WEAPON_TIMESTAMP = <ObjectiveUIStyle.CRAFTING_WEAPON_TIMESTAMP: 4>
CRAFTING_MEMENTOS = <ObjectiveUIStyle.CRAFTING_MEMENTOS: 5>
CRAFTING_MEMENTO_TITLE = <ObjectiveUIStyle.CRAFTING_MEMENTO_TITLE: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Place(builtins.int, aiobungie.Enum):
219@typing.final
220class Place(int, Enum):
221    """An Enum for Destiny 2 Places and NOT Planets"""
222
223    ORBIT = 2961497387
224    SOCIAL = 4151112093
225    LIGHT_HOUSE = 4276116472
226    EXPLORE = 3497767639

An Enum for Destiny 2 Places and NOT Planets

ORBIT = <Place.ORBIT: 2961497387>
SOCIAL = <Place.SOCIAL: 4151112093>
LIGHT_HOUSE = <Place.LIGHT_HOUSE: 4276116472>
EXPLORE = <Place.EXPLORE: 3497767639>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Planet(builtins.int, aiobungie.Enum):
184@typing.final
185class Planet(int, Enum):
186    """An Enum for all available planets in Destiny 2."""
187
188    UNKNOWN = 0
189    """Unknown space"""
190
191    EARTH = 3747705955
192    """Earth"""
193
194    DREAMING_CITY = 2877881518
195    """The Dreaming city."""
196
197    NESSUS = 3526908984
198    """Nessus"""
199
200    MOON = 3325508439
201    """The Moon"""
202
203    COSMODROME = 3990611421
204    """The Cosmodrome"""
205
206    TANGLED_SHORE = 3821439926
207    """The Tangled Shore"""
208
209    VENUS = 3871070152
210    """Venus"""
211
212    EAZ = 541863059  # Exclusive event.
213    """European Aerial Zone"""
214
215    EUROPA = 1729879943
216    """Europa"""

An Enum for all available planets in Destiny 2.

UNKNOWN = <Planet.UNKNOWN: 0>

Unknown space

EARTH = <Planet.EARTH: 3747705955>

Earth

DREAMING_CITY = <Planet.DREAMING_CITY: 2877881518>

The Dreaming city.

NESSUS = <Planet.NESSUS: 3526908984>

Nessus

MOON = <Planet.MOON: 3325508439>

The Moon

COSMODROME = <Planet.COSMODROME: 3990611421>

The Cosmodrome

TANGLED_SHORE = <Planet.TANGLED_SHORE: 3821439926>

The Tangled Shore

VENUS = <Planet.VENUS: 3871070152>

Venus

EAZ = <Planet.EAZ: 541863059>

European Aerial Zone

EUROPA = <Planet.EUROPA: 1729879943>

Europa

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Presence(builtins.int, aiobungie.Enum):
671@typing.final
672class Presence(int, Enum):
673    """An enum for a bungie friend status."""
674
675    OFFLINE_OR_UNKNOWN = 0
676    ONLINE = 1

An enum for a bungie friend status.

OFFLINE_OR_UNKNOWN = <Presence.OFFLINE_OR_UNKNOWN: 0>
ONLINE = <Presence.ONLINE: 1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class PrivacySetting(builtins.int, aiobungie.Enum):
759@typing.final
760class PrivacySetting(int, Enum):
761    """An enum for players's privacy settings."""
762
763    OPEN = 0
764    CLAN_AND_FRIENDS = 1
765    FRIENDS_ONLY = 2
766    INVITE_ONLY = 3
767    CLOSED = 4

An enum for players's privacy settings.

OPEN = <PrivacySetting.OPEN: 0>
CLAN_AND_FRIENDS = <PrivacySetting.CLAN_AND_FRIENDS: 1>
FRIENDS_ONLY = <PrivacySetting.FRIENDS_ONLY: 2>
INVITE_ONLY = <PrivacySetting.INVITE_ONLY: 3>
CLOSED = <PrivacySetting.CLOSED: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class RESTClient(aiobungie.interfaces.rest.RESTInterface):
 305class RESTClient(interfaces.RESTInterface):
 306    """A RESTful client implementation for Bungie's API.
 307
 308    This client is designed to only make HTTP requests and return JSON objects
 309    to provide RESTful functionality.
 310
 311    This client is the core for `aiobungie.Client` which deserialize those returned JSON objects
 312    using the factory into Pythonic data classes objects which provide Python functionality.
 313
 314    Example
 315    -------
 316    ```py
 317    import aiobungie
 318
 319    client = aiobungie.RESTClient("TOKEN")
 320    async with client:
 321        response = await client.fetch_clan_members(4389205)
 322        for member in response['results']:
 323            for key, value in member['destinyUserInfo'].items():
 324                print(key, value)
 325    ```
 326
 327    Parameters
 328    ----------
 329    token : `str`
 330        A valid application token from Bungie's developer portal.
 331
 332    Other Parameters
 333    ----------------
 334    max_retries : `int`
 335        The max retries number to retry if the request hit a `5xx` status code.
 336    client_secret : `str | None`
 337        An optional application client secret,
 338        This is only needed if you're fetching OAuth2 tokens with this client.
 339    client_id : `int | None`
 340        An optional application client id,
 341        This is only needed if you're fetching OAuth2 tokens with this client.
 342    enable_debugging : `bool | str`
 343        Whether to enable logging responses or not.
 344
 345    Logging Levels
 346    --------------
 347    * `False`: This will disable logging.
 348    * `True`: This will set the level to `DEBUG` and enable logging minimal information.
 349    * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information.
 350    """
 351
 352    __slots__ = (
 353        "_token",
 354        "_session",
 355        "_lock",
 356        "_max_retries",
 357        "_client_secret",
 358        "_client_id",
 359        "_metadata",
 360        "_dumps",
 361        "_loads",
 362    )
 363
 364    def __init__(
 365        self,
 366        token: str,
 367        /,
 368        *,
 369        client_secret: str | None = None,
 370        client_id: int | None = None,
 371        client_session: aiohttp.ClientSession | None = None,
 372        dumps: typedefs.Dumps = helpers.dumps,
 373        loads: typedefs.Loads = helpers.loads,
 374        max_retries: int = 4,
 375        enable_debugging: typing.Literal["TRACE"] | bool | int = False,
 376    ) -> None:
 377        self._session = client_session
 378        self._lock: asyncio.Lock | None = None
 379        self._client_secret = client_secret
 380        self._client_id = client_id
 381        self._token: str = token
 382        self._max_retries = max_retries
 383        self._dumps = dumps
 384        self._loads = loads
 385        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
 386
 387        self._set_debug_level(enable_debugging)
 388
 389    @property
 390    def client_id(self) -> int | None:
 391        return self._client_id
 392
 393    @property
 394    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
 395        return self._metadata
 396
 397    @property
 398    def is_alive(self) -> bool:
 399        return self._session is not None
 400
 401    @typing.final
 402    async def close(self) -> None:
 403        if self._session is None:
 404            raise RuntimeError("REST client is not running.")
 405
 406        await self._session.close()
 407        self._session = None
 408
 409    @typing.final
 410    def open(self) -> None:
 411        """Open a new client session. This is called internally with contextmanager usage."""
 412        if self._session:
 413            raise RuntimeError("Cannot open REST client when it's already open.")
 414
 415        self._session = aiohttp.ClientSession(
 416            connector=aiohttp.TCPConnector(),
 417            connector_owner=True,
 418            raise_for_status=False,
 419            timeout=aiohttp.ClientTimeout(total=30.0),
 420        )
 421
 422    @typing.final
 423    def enable_debugging(
 424        self,
 425        level: typing.Literal["TRACE"] | bool | int = False,
 426        file: pathlib.Path | str | None = None,
 427        /,
 428    ) -> None:
 429        self._set_debug_level(level, file)
 430
 431    @typing.final
 432    async def static_request(
 433        self,
 434        method: RequestMethod | str,
 435        path: str,
 436        *,
 437        auth: str | None = None,
 438        json: collections.Mapping[str, typing.Any] | None = None,
 439    ) -> typedefs.JSONIsh:
 440        return await self._request(method, path, auth=auth, json=json)
 441
 442    @typing.final
 443    def build_oauth2_url(
 444        self, client_id: int | None = None
 445    ) -> builders.OAuthURL | None:
 446        client_id = client_id or self._client_id
 447        if client_id is None:
 448            return None
 449
 450        return builders.OAuthURL(client_id=client_id)
 451
 452    @staticmethod
 453    def _set_debug_level(
 454        level: typing.Literal["TRACE"] | bool | int = False,
 455        file: pathlib.Path | str | None = None,
 456    ) -> None:
 457        file_handler = logging.FileHandler(file, mode="w") if file else None
 458        if level == "TRACE" or level == TRACE:
 459            logging.basicConfig(
 460                level=TRACE, handlers=[file_handler] if file_handler else None
 461            )
 462
 463        elif level:
 464            logging.basicConfig(
 465                level=logging.DEBUG, handlers=[file_handler] if file_handler else None
 466            )
 467
 468    async def _request(
 469        self,
 470        method: RequestMethod | str,
 471        route: str,
 472        *,
 473        base: bool = False,
 474        oauth2: bool = False,
 475        auth: str | None = None,
 476        unwrapping: typing.Literal["json", "read"] = "json",
 477        json: collections.Mapping[str, typing.Any] | str | None = None,
 478        params: collections.Mapping[str, typing.Any] | None = None,
 479        headers: dict[str, typing.Any] | None = None,
 480    ) -> typedefs.JSONIsh:
 481        # This is not None when opening the client.
 482        assert self._session is not None
 483
 484        retries: int = 0
 485        headers = headers or {}
 486
 487        headers.setdefault(_USER_AGENT_HEADERS, _USER_AGENT)
 488        headers["X-API-KEY"] = self._token
 489
 490        if auth is not None:
 491            headers[_AUTH_HEADER] = f"Bearer {auth}"
 492
 493        # Handling endpoints
 494        endpoint = url.BASE
 495
 496        if not base:
 497            endpoint = endpoint + url.REST_EP
 498
 499        if oauth2:
 500            headers["Content-Type"] = "application/x-www-form-urlencoded"
 501            endpoint = endpoint + url.TOKEN_EP
 502
 503        if self._lock is None:
 504            self._lock = asyncio.Lock()
 505
 506        while True:
 507            async with (stack := contextlib.AsyncExitStack()):
 508                await stack.enter_async_context(self._lock)
 509
 510                data = self._dumps(json) if isinstance(json, dict) else json
 511                # We make the request here.
 512                taken_time = time.monotonic()
 513                response = await self._session.request(
 514                    method=method,
 515                    url=f"{endpoint}/{route}",
 516                    headers=headers,
 517                    data=data,
 518                    params=params,
 519                )
 520                response_time = (time.monotonic() - taken_time) * 1_000
 521
 522                _LOG.debug(
 523                    "%s %s %s Time %.4fms",
 524                    method,
 525                    f"{endpoint}/{route}",
 526                    f"{response.status} {response.reason}",
 527                    response_time,
 528                )
 529
 530                await self._handle_ratelimit(response, method, route)
 531
 532                if response.status == http.HTTPStatus.NO_CONTENT:
 533                    return None
 534
 535                if 300 > response.status >= 200:
 536                    if unwrapping == "read":
 537                        # We need to read the bytes for the manifest response.
 538                        return await response.read()
 539
 540                    if response.content_type == _APP_JSON:
 541                        # json_data = self._loads(await response.read())
 542                        json_data = self._loads(await response.read())
 543
 544                        _LOG.debug(
 545                            "%s %s %s Time %.4fms",
 546                            method,
 547                            f"{endpoint}/{route}",
 548                            f"{response.status} {response.reason}",
 549                            response_time,
 550                        )
 551
 552                        if _LOG.isEnabledFor(TRACE):
 553                            cloned = headers.copy()
 554                            cloned.update(response.headers)  # type: ignore
 555
 556                            _LOG.log(
 557                                TRACE,
 558                                "%s",
 559                                error.stringify_http_message(cloned),
 560                            )
 561
 562                        # Return the response.
 563                        # oauth2 responses are not packed inside a Response object.
 564                        if oauth2:
 565                            return json_data  # type: ignore[no-any-return]
 566
 567                        return json_data["Response"]  # type: ignore
 568
 569                if (
 570                    response.status in _RETRY_5XX and retries < self._max_retries  # noqa: W503
 571                ):
 572                    backoff_ = backoff.ExponentialBackOff(maximum=6)
 573                    sleep_time = next(backoff_)
 574                    _LOG.warning(
 575                        "Got %i - %s. Sleeping for %.2f seconds. Remaining retries: %i",
 576                        response.status,
 577                        response.reason,
 578                        sleep_time,
 579                        self._max_retries - retries,
 580                    )
 581
 582                    retries += 1
 583                    await asyncio.sleep(sleep_time)
 584                    continue
 585
 586                raise await error.raise_error(response)
 587
 588    async def __aenter__(self) -> RESTClient:
 589        self.open()
 590        return self
 591
 592    async def __aexit__(
 593        self,
 594        exception_type: type[BaseException] | None,
 595        exception: BaseException | None,
 596        exception_traceback: types.TracebackType | None,
 597    ) -> None:
 598        await self.close()
 599
 600    # We don't want this to be super complicated.
 601    @typing.final
 602    async def _handle_ratelimit(
 603        self,
 604        response: aiohttp.ClientResponse,
 605        method: str,
 606        route: str,
 607    ) -> None:
 608        if response.status != http.HTTPStatus.TOO_MANY_REQUESTS:
 609            return
 610
 611        if response.content_type != _APP_JSON:
 612            raise error.HTTPError(
 613                f"Being ratelimited on non JSON request, {response.content_type}.",
 614                http.HTTPStatus.TOO_MANY_REQUESTS,
 615            )
 616
 617        json: typedefs.JSONObject = self._loads(await response.read())  # type: ignore
 618        retry_after = float(json.get("ThrottleSeconds", 15.0)) + 0.1
 619        max_calls: int = 0
 620
 621        while True:
 622            if max_calls == 10:
 623                # Max retries by default. We raise an error here.
 624                raise error.RateLimitedError(
 625                    body=json,
 626                    url=str(response.real_url),
 627                    retry_after=retry_after,
 628                )
 629
 630            # We sleep for a little bit to avoid funky behavior.
 631            _LOG.warning(
 632                "We're being ratelimited, Method %s Route %s. Sleeping for %.2fs.",
 633                method,
 634                route,
 635                retry_after,
 636            )
 637            await asyncio.sleep(retry_after)
 638            max_calls += 1
 639            continue
 640
 641    async def fetch_oauth2_tokens(self, code: str, /) -> builders.OAuth2Response:
 642        if not isinstance(self._client_secret, (str, int)):
 643            raise TypeError(
 644                "Expected (str, int) for client secret "
 645                f"but got {type(self._client_secret).__name__}"  # type: ignore
 646            )
 647
 648        headers = {
 649            "client_secret": self._client_secret,
 650        }
 651
 652        data = (
 653            f"grant_type=authorization_code&code={code}"
 654            f"&client_id={self._client_id}&client_secret={self._client_secret}"
 655        )
 656
 657        response = await self._request(
 658            RequestMethod.POST, "", headers=headers, json=data, oauth2=True
 659        )
 660        assert isinstance(response, dict)
 661        return builders.OAuth2Response.build_response(response)
 662
 663    async def refresh_access_token(
 664        self, refresh_token: str, /
 665    ) -> builders.OAuth2Response:
 666        if not isinstance(self._client_secret, (int, str)):
 667            raise TypeError(
 668                f"Expected (str, int) for client secret but got {type(self._client_secret).__name__}"  # type: ignore
 669            )
 670
 671        data = {
 672            "grant_type": "refresh_token",
 673            "refresh_token": refresh_token,
 674            "client_id": self._client_id,
 675            "client_secret": self._client_secret,
 676            "Content-Type": "application/x-www-form-urlencoded",
 677        }
 678
 679        response = await self._request(RequestMethod.POST, "", json=data, oauth2=True)
 680        assert isinstance(response, dict)
 681        return builders.OAuth2Response.build_response(response)
 682
 683    async def fetch_bungie_user(self, id: int) -> typedefs.JSONObject:
 684        resp = await self._request(
 685            RequestMethod.GET, f"User/GetBungieNetUserById/{id}/"
 686        )
 687        assert isinstance(resp, dict)
 688        return resp
 689
 690    async def fetch_user_themes(self) -> typedefs.JSONArray:
 691        resp = await self._request(RequestMethod.GET, "User/GetAvailableThemes/")
 692        assert isinstance(resp, list)
 693        return resp
 694
 695    async def fetch_membership_from_id(
 696        self,
 697        id: int,
 698        type: enums.MembershipType | int = enums.MembershipType.NONE,
 699        /,
 700    ) -> typedefs.JSONObject:
 701        resp = await self._request(
 702            RequestMethod.GET, f"User/GetMembershipsById/{id}/{int(type)}"
 703        )
 704        assert isinstance(resp, dict)
 705        return resp
 706
 707    async def fetch_membership(
 708        self,
 709        name: str,
 710        code: int,
 711        type: enums.MembershipType | int = enums.MembershipType.ALL,
 712        /,
 713    ) -> typedefs.JSONArray:
 714        resp = await self._request(
 715            RequestMethod.POST,
 716            f"Destiny2/SearchDestinyPlayerByBungieName/{int(type)}",
 717            json={"displayName": name, "displayNameCode": code},
 718        )
 719        assert isinstance(resp, list)
 720        return resp
 721
 722    async def search_users(self, name: str, /) -> typedefs.JSONObject:
 723        resp = await self._request(
 724            RequestMethod.POST,
 725            "User/Search/GlobalName/0",
 726            json={"displayNamePrefix": name},
 727        )
 728        assert isinstance(resp, dict)
 729        return resp
 730
 731    async def fetch_clan_from_id(
 732        self, id: int, /, access_token: str | None = None
 733    ) -> typedefs.JSONObject:
 734        resp = await self._request(
 735            RequestMethod.GET, f"GroupV2/{id}", auth=access_token
 736        )
 737        assert isinstance(resp, dict)
 738        return resp
 739
 740    async def fetch_clan(
 741        self,
 742        name: str,
 743        /,
 744        access_token: str | None = None,
 745        *,
 746        type: enums.GroupType | int = enums.GroupType.CLAN,
 747    ) -> typedefs.JSONObject:
 748        resp = await self._request(
 749            RequestMethod.GET, f"GroupV2/Name/{name}/{int(type)}", auth=access_token
 750        )
 751        assert isinstance(resp, dict)
 752        return resp
 753
 754    async def fetch_clan_admins(self, clan_id: int, /) -> typedefs.JSONObject:
 755        resp = await self._request(
 756            RequestMethod.GET, f"GroupV2/{clan_id}/AdminsAndFounder/"
 757        )
 758        assert isinstance(resp, dict)
 759        return resp
 760
 761    async def fetch_clan_conversations(self, clan_id: int, /) -> typedefs.JSONArray:
 762        resp = await self._request(
 763            RequestMethod.GET, f"GroupV2/{clan_id}/OptionalConversations/"
 764        )
 765        assert isinstance(resp, list)
 766        return resp
 767
 768    async def fetch_application(self, appid: int, /) -> typedefs.JSONObject:
 769        resp = await self._request(RequestMethod.GET, f"App/Application/{appid}")
 770        assert isinstance(resp, dict)
 771        return resp
 772
 773    async def fetch_character(
 774        self,
 775        member_id: int,
 776        membership_type: enums.MembershipType | int,
 777        character_id: int,
 778        components: list[enums.ComponentType],
 779        auth: str | None = None,
 780    ) -> typedefs.JSONObject:
 781        collector = _collect_components(components)
 782        response = await self._request(
 783            RequestMethod.GET,
 784            f"Destiny2/{int(membership_type)}/Profile/{member_id}/"
 785            f"Character/{character_id}/?components={collector}",
 786            auth=auth,
 787        )
 788        assert isinstance(response, dict)
 789        return response
 790
 791    async def fetch_activities(
 792        self,
 793        member_id: int,
 794        character_id: int,
 795        mode: enums.GameMode | int,
 796        membership_type: enums.MembershipType | int = enums.MembershipType.ALL,
 797        *,
 798        page: int = 0,
 799        limit: int = 1,
 800    ) -> typedefs.JSONObject:
 801        resp = await self._request(
 802            RequestMethod.GET,
 803            f"Destiny2/{int(membership_type)}/Account/"
 804            f"{member_id}/Character/{character_id}/Stats/Activities"
 805            f"/?mode={int(mode)}&count={limit}&page={page}",
 806        )
 807        assert isinstance(resp, dict)
 808        return resp
 809
 810    async def fetch_vendor_sales(self) -> typedefs.JSONObject:
 811        resp = await self._request(
 812            RequestMethod.GET,
 813            f"Destiny2/Vendors/?components={int(enums.ComponentType.VENDOR_SALES)}",
 814        )
 815        assert isinstance(resp, dict)
 816        return resp
 817
 818    async def fetch_profile(
 819        self,
 820        membership_id: int,
 821        type: enums.MembershipType | int,
 822        components: list[enums.ComponentType],
 823        auth: str | None = None,
 824    ) -> typedefs.JSONObject:
 825        collector = _collect_components(components)
 826        response = await self._request(
 827            RequestMethod.GET,
 828            f"Destiny2/{int(type)}/Profile/{membership_id}/?components={collector}",
 829            auth=auth,
 830        )
 831        assert isinstance(response, dict)
 832        return response
 833
 834    async def fetch_entity(self, type: str, hash: int) -> typedefs.JSONObject:
 835        response = await self._request(
 836            RequestMethod.GET, route=f"Destiny2/Manifest/{type}/{hash}"
 837        )
 838        assert isinstance(response, dict)
 839        return response
 840
 841    async def fetch_inventory_item(self, hash: int, /) -> typedefs.JSONObject:
 842        resp = await self.fetch_entity("DestinyInventoryItemDefinition", hash)
 843        assert isinstance(resp, dict)
 844        return resp
 845
 846    async def fetch_objective_entity(self, hash: int, /) -> typedefs.JSONObject:
 847        resp = await self.fetch_entity("DestinyObjectiveDefinition", hash)
 848        assert isinstance(resp, dict)
 849        return resp
 850
 851    async def fetch_groups_for_member(
 852        self,
 853        member_id: int,
 854        member_type: enums.MembershipType | int,
 855        /,
 856        *,
 857        filter: int = 0,
 858        group_type: enums.GroupType | int = enums.GroupType.CLAN,
 859    ) -> typedefs.JSONObject:
 860        resp = await self._request(
 861            RequestMethod.GET,
 862            f"GroupV2/User/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
 863        )
 864        assert isinstance(resp, dict)
 865        return resp
 866
 867    async def fetch_potential_groups_for_member(
 868        self,
 869        member_id: int,
 870        member_type: enums.MembershipType | int,
 871        /,
 872        *,
 873        filter: int = 0,
 874        group_type: enums.GroupType | int = enums.GroupType.CLAN,
 875    ) -> typedefs.JSONObject:
 876        resp = await self._request(
 877            RequestMethod.GET,
 878            f"GroupV2/User/Potential/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
 879        )
 880        assert isinstance(resp, dict)
 881        return resp
 882
 883    async def fetch_clan_members(
 884        self,
 885        clan_id: int,
 886        /,
 887        *,
 888        name: str | None = None,
 889        type: enums.MembershipType | int = enums.MembershipType.NONE,
 890    ) -> typedefs.JSONObject:
 891        resp = await self._request(
 892            RequestMethod.GET,
 893            f"/GroupV2/{clan_id}/Members/?memberType={int(type)}&nameSearch={name if name else ''}&currentpage=1",
 894        )
 895        assert isinstance(resp, dict)
 896        return resp
 897
 898    async def fetch_hardlinked_credentials(
 899        self,
 900        credential: int,
 901        type: enums.CredentialType | int = enums.CredentialType.STEAMID,
 902        /,
 903    ) -> typedefs.JSONObject:
 904        resp = await self._request(
 905            RequestMethod.GET,
 906            f"User/GetMembershipFromHardLinkedCredential/{int(type)}/{credential}/",
 907        )
 908        assert isinstance(resp, dict)
 909        return resp
 910
 911    async def fetch_user_credentials(
 912        self, access_token: str, membership_id: int, /
 913    ) -> typedefs.JSONArray:
 914        resp = await self._request(
 915            RequestMethod.GET,
 916            f"User/GetCredentialTypesForTargetAccount/{membership_id}",
 917            auth=access_token,
 918        )
 919        assert isinstance(resp, list)
 920        return resp
 921
 922    async def insert_socket_plug(
 923        self,
 924        action_token: str,
 925        /,
 926        instance_id: int,
 927        plug: builders.PlugSocketBuilder | collections.Mapping[str, int],
 928        character_id: int,
 929        membership_type: enums.MembershipType | int,
 930    ) -> typedefs.JSONObject:
 931        if isinstance(plug, builders.PlugSocketBuilder):
 932            plug = plug.collect()
 933
 934        body = {
 935            "actionToken": action_token,
 936            "itemInstanceId": instance_id,
 937            "plug": plug,
 938            "characterId": character_id,
 939            "membershipType": int(membership_type),
 940        }
 941        resp = await self._request(
 942            RequestMethod.POST, "Destiny2/Actions/Items/InsertSocketPlug", json=body
 943        )
 944        assert isinstance(resp, dict)
 945        return resp
 946
 947    async def insert_socket_plug_free(
 948        self,
 949        access_token: str,
 950        /,
 951        instance_id: int,
 952        plug: builders.PlugSocketBuilder | collections.Mapping[str, int],
 953        character_id: int,
 954        membership_type: enums.MembershipType | int,
 955    ) -> typedefs.JSONObject:
 956        if isinstance(plug, builders.PlugSocketBuilder):
 957            plug = plug.collect()
 958
 959        body = {
 960            "itemInstanceId": instance_id,
 961            "plug": plug,
 962            "characterId": character_id,
 963            "membershipType": int(membership_type),
 964        }
 965        resp = await self._request(
 966            RequestMethod.POST,
 967            "Destiny2/Actions/Items/InsertSocketPlugFree",
 968            json=body,
 969            auth=access_token,
 970        )
 971        assert isinstance(resp, dict)
 972        return resp
 973
 974    async def set_item_lock_state(
 975        self,
 976        access_token: str,
 977        state: bool,
 978        /,
 979        item_id: int,
 980        character_id: int,
 981        membership_type: enums.MembershipType | int,
 982    ) -> int:
 983        body = {
 984            "state": state,
 985            "itemId": item_id,
 986            "characterId": character_id,
 987            "membershipType": int(membership_type),
 988        }
 989        response = await self._request(
 990            RequestMethod.POST,
 991            "Destiny2/Actions/Items/SetLockState",
 992            json=body,
 993            auth=access_token,
 994        )
 995        assert isinstance(response, int)
 996        return response
 997
 998    async def set_quest_track_state(
 999        self,
1000        access_token: str,
1001        state: bool,
1002        /,
1003        item_id: int,
1004        character_id: int,
1005        membership_type: enums.MembershipType | int,
1006    ) -> int:
1007        body = {
1008            "state": state,
1009            "itemId": item_id,
1010            "characterId": character_id,
1011            "membership_type": int(membership_type),
1012        }
1013        response = await self._request(
1014            RequestMethod.POST,
1015            "Destiny2/Actions/Items/SetTrackedState",
1016            json=body,
1017            auth=access_token,
1018        )
1019        assert isinstance(response, int)
1020        return response
1021
1022    async def fetch_manifest_path(self) -> typedefs.JSONObject:
1023        path = await self._request(RequestMethod.GET, "Destiny2/Manifest")
1024        assert isinstance(path, dict)
1025        return path
1026
1027    async def read_manifest_bytes(self, language: str = "en", /) -> bytes:
1028        _ensure_manifest_language(language)
1029
1030        content = await self.fetch_manifest_path()
1031        resp = await self._request(
1032            RequestMethod.GET,
1033            content["mobileWorldContentPaths"][language],
1034            unwrapping="read",
1035            base=True,
1036        )
1037        assert isinstance(resp, bytes)
1038        return resp
1039
1040    async def download_sqlite_manifest(
1041        self,
1042        language: str = "en",
1043        name: str = "manifest",
1044        path: pathlib.Path | str = ".",
1045        *,
1046        force: bool = False,
1047    ) -> pathlib.Path:
1048        complete_path = _get_path(name, path, sql=True)
1049
1050        if complete_path.exists() and force:
1051            if force:
1052                _LOG.info(
1053                    f"Found manifest in {complete_path!s}. Forcing to Re-Download."
1054                )
1055                complete_path.unlink(missing_ok=True)
1056
1057                return await self.download_sqlite_manifest(
1058                    language, name, path, force=force
1059                )
1060
1061            else:
1062                raise FileExistsError(
1063                    "Manifest file already exists, "
1064                    "To force download, set the `force` parameter to `True`."
1065                )
1066
1067        _LOG.info(f"Downloading manifest. Location: {complete_path!s}")
1068        data_bytes = await self.read_manifest_bytes(language)
1069        await asyncio.get_running_loop().run_in_executor(
1070            None, _write_sqlite_bytes, data_bytes, path, name
1071        )
1072        return _get_path(name, path, sql=True)
1073
1074    async def download_json_manifest(
1075        self,
1076        file_name: str = "manifest",
1077        path: str | pathlib.Path = ".",
1078        language: str = "en",
1079    ) -> pathlib.Path:
1080        _ensure_manifest_language(language)
1081
1082        _LOG.info(f"Downloading manifest JSON to {_get_path(file_name, path)!r}...")
1083
1084        content = await self.fetch_manifest_path()
1085        json_bytes = await self._request(
1086            RequestMethod.GET,
1087            content["jsonWorldContentPaths"][language],
1088            unwrapping="read",
1089            base=True,
1090        )
1091
1092        await asyncio.get_running_loop().run_in_executor(
1093            None, _write_json_bytes, json_bytes, file_name, path
1094        )
1095        _LOG.info("Finished downloading manifest JSON.")
1096        return _get_path(file_name, path)
1097
1098    async def fetch_manifest_version(self) -> str:
1099        # This is guaranteed str.
1100        return (await self.fetch_manifest_path())["version"]  # type: ignore[no-any-return]
1101
1102    async def fetch_linked_profiles(
1103        self,
1104        member_id: int,
1105        member_type: enums.MembershipType | int,
1106        /,
1107        *,
1108        all: bool = False,
1109    ) -> typedefs.JSONObject:
1110        resp = await self._request(
1111            RequestMethod.GET,
1112            f"Destiny2/{int(member_type)}/Profile/{member_id}/LinkedProfiles/?getAllMemberships={all}",
1113        )
1114        assert isinstance(resp, dict)
1115        return resp
1116
1117    async def fetch_clan_banners(self) -> typedefs.JSONObject:
1118        resp = await self._request(
1119            RequestMethod.GET, "Destiny2/Clan/ClanBannerDictionary/"
1120        )
1121        assert isinstance(resp, dict)
1122        return resp
1123
1124    async def fetch_public_milestones(self) -> typedefs.JSONObject:
1125        resp = await self._request(RequestMethod.GET, "Destiny2/Milestones/")
1126        assert isinstance(resp, dict)
1127        return resp
1128
1129    async def fetch_public_milestone_content(
1130        self, milestone_hash: int, /
1131    ) -> typedefs.JSONObject:
1132        resp = await self._request(
1133            RequestMethod.GET, f"Destiny2/Milestones/{milestone_hash}/Content/"
1134        )
1135        assert isinstance(resp, dict)
1136        return resp
1137
1138    async def fetch_current_user_memberships(
1139        self, access_token: str, /
1140    ) -> typedefs.JSONObject:
1141        resp = await self._request(
1142            RequestMethod.GET,
1143            "User/GetMembershipsForCurrentUser/",
1144            auth=access_token,
1145        )
1146        assert isinstance(resp, dict)
1147        return resp
1148
1149    async def equip_item(
1150        self,
1151        access_token: str,
1152        /,
1153        item_id: int,
1154        character_id: int,
1155        membership_type: enums.MembershipType | int,
1156    ) -> None:
1157        payload = {
1158            "itemId": item_id,
1159            "characterId": character_id,
1160            "membershipType": int(membership_type),
1161        }
1162
1163        await self._request(
1164            RequestMethod.POST,
1165            "Destiny2/Actions/Items/EquipItem/",
1166            json=payload,
1167            auth=access_token,
1168        )
1169
1170    async def equip_items(
1171        self,
1172        access_token: str,
1173        /,
1174        item_ids: collections.Sequence[int],
1175        character_id: int,
1176        membership_type: enums.MembershipType | int,
1177    ) -> None:
1178        payload = {
1179            "itemIds": item_ids,
1180            "characterId": character_id,
1181            "membershipType": int(membership_type),
1182        }
1183        await self._request(
1184            RequestMethod.POST,
1185            "Destiny2/Actions/Items/EquipItems/",
1186            json=payload,
1187            auth=access_token,
1188        )
1189
1190    async def ban_clan_member(
1191        self,
1192        access_token: str,
1193        /,
1194        group_id: int,
1195        membership_id: int,
1196        membership_type: enums.MembershipType | int,
1197        *,
1198        length: int = 0,
1199        comment: str | None = None,
1200    ) -> None:
1201        payload = {"comment": str(comment), "length": length}
1202        await self._request(
1203            RequestMethod.POST,
1204            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Ban/",
1205            json=payload,
1206            auth=access_token,
1207        )
1208
1209    async def unban_clan_member(
1210        self,
1211        access_token: str,
1212        /,
1213        group_id: int,
1214        membership_id: int,
1215        membership_type: enums.MembershipType | int,
1216    ) -> None:
1217        await self._request(
1218            RequestMethod.POST,
1219            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Unban/",
1220            auth=access_token,
1221        )
1222
1223    async def kick_clan_member(
1224        self,
1225        access_token: str,
1226        /,
1227        group_id: int,
1228        membership_id: int,
1229        membership_type: enums.MembershipType | int,
1230    ) -> typedefs.JSONObject:
1231        resp = await self._request(
1232            RequestMethod.POST,
1233            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Kick/",
1234            auth=access_token,
1235        )
1236        assert isinstance(resp, dict)
1237        return resp
1238
1239    async def edit_clan(
1240        self,
1241        access_token: str,
1242        /,
1243        group_id: int,
1244        *,
1245        name: str | None = None,
1246        about: str | None = None,
1247        motto: str | None = None,
1248        theme: str | None = None,
1249        tags: collections.Sequence[str] | None = None,
1250        is_public: bool | None = None,
1251        locale: str | None = None,
1252        avatar_image_index: int | None = None,
1253        membership_option: enums.MembershipOption | int | None = None,
1254        allow_chat: bool | None = None,
1255        chat_security: typing.Literal[0, 1] | None = None,
1256        call_sign: str | None = None,
1257        homepage: typing.Literal[0, 1, 2] | None = None,
1258        enable_invite_messaging_for_admins: bool | None = None,
1259        default_publicity: typing.Literal[0, 1, 2] | None = None,
1260        is_public_topic_admin: bool | None = None,
1261    ) -> None:
1262        payload = {
1263            "name": name,
1264            "about": about,
1265            "motto": motto,
1266            "theme": theme,
1267            "tags": tags,
1268            "isPublic": is_public,
1269            "avatarImageIndex": avatar_image_index,
1270            "isPublicTopicAdminOnly": is_public_topic_admin,
1271            "allowChat": allow_chat,
1272            "chatSecurity": chat_security,
1273            "callsign": call_sign,
1274            "homepage": homepage,
1275            "enableInvitationMessagingForAdmins": enable_invite_messaging_for_admins,
1276            "defaultPublicity": default_publicity,
1277            "locale": locale,
1278        }
1279        if membership_option is not None:
1280            payload["membershipOption"] = int(membership_option)
1281
1282        await self._request(
1283            RequestMethod.POST,
1284            f"GroupV2/{group_id}/Edit",
1285            json=payload,
1286            auth=access_token,
1287        )
1288
1289    async def edit_clan_options(
1290        self,
1291        access_token: str,
1292        /,
1293        group_id: int,
1294        *,
1295        invite_permissions_override: bool | None = None,
1296        update_culture_permissionOverride: bool | None = None,
1297        host_guided_game_permission_override: typing.Literal[0, 1, 2] | None = None,
1298        update_banner_permission_override: bool | None = None,
1299        join_level: enums.ClanMemberType | int | None = None,
1300    ) -> None:
1301        payload = {
1302            "InvitePermissionOverride": invite_permissions_override,
1303            "UpdateCulturePermissionOverride": update_culture_permissionOverride,
1304            "HostGuidedGamePermissionOverride": host_guided_game_permission_override,
1305            "UpdateBannerPermissionOverride": update_banner_permission_override,
1306            "JoinLevel": int(join_level) if join_level else None,
1307        }
1308
1309        await self._request(
1310            RequestMethod.POST,
1311            f"GroupV2/{group_id}/EditFounderOptions",
1312            json=payload,
1313            auth=access_token,
1314        )
1315
1316    async def fetch_friends(self, access_token: str, /) -> typedefs.JSONObject:
1317        resp = await self._request(
1318            RequestMethod.GET,
1319            "Social/Friends/",
1320            auth=access_token,
1321        )
1322        assert isinstance(resp, dict)
1323        return resp
1324
1325    async def fetch_friend_requests(self, access_token: str, /) -> typedefs.JSONObject:
1326        resp = await self._request(
1327            RequestMethod.GET,
1328            "Social/Friends/Requests",
1329            auth=access_token,
1330        )
1331        assert isinstance(resp, dict)
1332        return resp
1333
1334    async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1335        await self._request(
1336            RequestMethod.POST,
1337            f"Social/Friends/Requests/Accept/{member_id}",
1338            auth=access_token,
1339        )
1340
1341    async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1342        await self._request(
1343            RequestMethod.POST,
1344            f"Social/Friends/Add/{member_id}",
1345            auth=access_token,
1346        )
1347
1348    async def decline_friend_request(
1349        self, access_token: str, /, member_id: int
1350    ) -> None:
1351        await self._request(
1352            RequestMethod.POST,
1353            f"Social/Friends/Requests/Decline/{member_id}",
1354            auth=access_token,
1355        )
1356
1357    async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1358        await self._request(
1359            RequestMethod.POST,
1360            f"Social/Friends/Remove/{member_id}",
1361            auth=access_token,
1362        )
1363
1364    async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1365        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1366        await self._request(
1367            RequestMethod.POST,
1368            f"Social/Friends/Requests/Remove/{member_id}",
1369            auth=access_token,
1370        )
1371
1372    async def approve_all_pending_group_users(
1373        self,
1374        access_token: str,
1375        /,
1376        group_id: int,
1377        message: str | None = None,
1378    ) -> None:
1379        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1380        await self._request(
1381            RequestMethod.POST,
1382            f"GroupV2/{group_id}/Members/ApproveAll",
1383            auth=access_token,
1384            json={"message": str(message)},
1385        )
1386
1387    async def deny_all_pending_group_users(
1388        self,
1389        access_token: str,
1390        /,
1391        group_id: int,
1392        *,
1393        message: str | None = None,
1394    ) -> None:
1395        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1396        await self._request(
1397            RequestMethod.POST,
1398            f"GroupV2/{group_id}/Members/DenyAll",
1399            auth=access_token,
1400            json={"message": str(message)},
1401        )
1402
1403    async def add_optional_conversation(
1404        self,
1405        access_token: str,
1406        /,
1407        group_id: int,
1408        *,
1409        name: str | None = None,
1410        security: typing.Literal[0, 1] = 0,
1411    ) -> None:
1412        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1413        payload = {"chatName": str(name), "chatSecurity": security}
1414        await self._request(
1415            RequestMethod.POST,
1416            f"GroupV2/{group_id}/OptionalConversations/Add",
1417            json=payload,
1418            auth=access_token,
1419        )
1420
1421    async def edit_optional_conversation(
1422        self,
1423        access_token: str,
1424        /,
1425        group_id: int,
1426        conversation_id: int,
1427        *,
1428        name: str | None = None,
1429        security: typing.Literal[0, 1] = 0,
1430        enable_chat: bool = False,
1431    ) -> None:
1432        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1433        payload = {
1434            "chatEnabled": enable_chat,
1435            "chatName": str(name),
1436            "chatSecurity": security,
1437        }
1438        await self._request(
1439            RequestMethod.POST,
1440            f"GroupV2/{group_id}/OptionalConversations/Edit/{conversation_id}",
1441            json=payload,
1442            auth=access_token,
1443        )
1444
1445    async def transfer_item(
1446        self,
1447        access_token: str,
1448        /,
1449        item_id: int,
1450        item_hash: int,
1451        character_id: int,
1452        member_type: enums.MembershipType | int,
1453        *,
1454        stack_size: int = 1,
1455        vault: bool = False,
1456    ) -> None:
1457        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1458        payload = {
1459            "characterId": character_id,
1460            "membershipType": int(member_type),
1461            "itemId": item_id,
1462            "itemReferenceHash": item_hash,
1463            "stackSize": stack_size,
1464            "transferToVault": vault,
1465        }
1466        await self._request(
1467            RequestMethod.POST,
1468            "Destiny2/Actions/Items/TransferItem",
1469            json=payload,
1470            auth=access_token,
1471        )
1472
1473    async def pull_item(
1474        self,
1475        access_token: str,
1476        /,
1477        item_id: int,
1478        item_hash: int,
1479        character_id: int,
1480        member_type: enums.MembershipType | int,
1481        *,
1482        stack_size: int = 1,
1483        vault: bool = False,
1484    ) -> None:
1485        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1486        payload = {
1487            "characterId": character_id,
1488            "membershipType": int(member_type),
1489            "itemId": item_id,
1490            "itemReferenceHash": item_hash,
1491            "stackSize": stack_size,
1492            "transferToVault": vault,
1493        }
1494        await self._request(
1495            RequestMethod.POST,
1496            "Destiny2/Actions/Items/PullFromPostmaster",
1497            json=payload,
1498            auth=access_token,
1499        )
1500
1501    async def fetch_fireteams(
1502        self,
1503        activity_type: fireteams.FireteamActivity | int,
1504        *,
1505        platform: fireteams.FireteamPlatform | int = fireteams.FireteamPlatform.ANY,
1506        language: fireteams.FireteamLanguage | str = fireteams.FireteamLanguage.ALL,
1507        date_range: fireteams.FireteamDate | int = fireteams.FireteamDate.ALL,
1508        page: int = 0,
1509        slots_filter: int = 0,
1510    ) -> typedefs.JSONObject:
1511        resp = await self._request(
1512            RequestMethod.GET,
1513            f"Fireteam/Search/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{page}/?langFilter={str(language)}",  # noqa: E501 Line too long
1514        )
1515        assert isinstance(resp, dict)
1516        return resp
1517
1518    async def fetch_available_clan_fireteams(
1519        self,
1520        access_token: str,
1521        group_id: int,
1522        activity_type: fireteams.FireteamActivity | int,
1523        *,
1524        platform: fireteams.FireteamPlatform | int,
1525        language: fireteams.FireteamLanguage | str,
1526        date_range: fireteams.FireteamDate | int = fireteams.FireteamDate.ALL,
1527        page: int = 0,
1528        public_only: bool = False,
1529        slots_filter: int = 0,
1530    ) -> typedefs.JSONObject:
1531        resp = await self._request(
1532            RequestMethod.GET,
1533            f"Fireteam/Clan/{group_id}/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{public_only}/{page}",  # noqa: E501
1534            json={"langFilter": str(language)},
1535            auth=access_token,
1536        )
1537        assert isinstance(resp, dict)
1538        return resp
1539
1540    async def fetch_clan_fireteam(
1541        self, access_token: str, fireteam_id: int, group_id: int
1542    ) -> typedefs.JSONObject:
1543        resp = await self._request(
1544            RequestMethod.GET,
1545            f"Fireteam/Clan/{group_id}/Summary/{fireteam_id}",
1546            auth=access_token,
1547        )
1548        assert isinstance(resp, dict)
1549        return resp
1550
1551    async def fetch_my_clan_fireteams(
1552        self,
1553        access_token: str,
1554        group_id: int,
1555        *,
1556        include_closed: bool = True,
1557        platform: fireteams.FireteamPlatform | int,
1558        language: fireteams.FireteamLanguage | str,
1559        filtered: bool = True,
1560        page: int = 0,
1561    ) -> typedefs.JSONObject:
1562        payload = {"groupFilter": filtered, "langFilter": str(language)}
1563
1564        resp = await self._request(
1565            RequestMethod.GET,
1566            f"Fireteam/Clan/{group_id}/My/{int(platform)}/{include_closed}/{page}",
1567            json=payload,
1568            auth=access_token,
1569        )
1570        assert isinstance(resp, dict)
1571        return resp
1572
1573    async def fetch_private_clan_fireteams(
1574        self, access_token: str, group_id: int, /
1575    ) -> int:
1576        resp = await self._request(
1577            RequestMethod.GET,
1578            f"Fireteam/Clan/{group_id}/ActiveCount",
1579            auth=access_token,
1580        )
1581        assert isinstance(resp, int)
1582        return resp
1583
1584    async def fetch_post_activity(self, instance_id: int, /) -> typedefs.JSONObject:
1585        resp = await self._request(
1586            RequestMethod.GET, f"Destiny2/Stats/PostGameCarnageReport/{instance_id}"
1587        )
1588        assert isinstance(resp, dict)
1589        return resp
1590
1591    async def search_entities(
1592        self, name: str, entity_type: str, *, page: int = 0
1593    ) -> typedefs.JSONObject:
1594        resp = await self._request(
1595            RequestMethod.GET,
1596            f"Destiny2/Armory/Search/{entity_type}/{name}/",
1597            json={"page": page},
1598        )
1599        assert isinstance(resp, dict)
1600        return resp
1601
1602    async def fetch_unique_weapon_history(
1603        self,
1604        membership_id: int,
1605        character_id: int,
1606        membership_type: enums.MembershipType | int,
1607    ) -> typedefs.JSONObject:
1608        resp = await self._request(
1609            RequestMethod.GET,
1610            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/UniqueWeapons/",
1611        )
1612        assert isinstance(resp, dict)
1613        return resp
1614
1615    async def fetch_item(
1616        self,
1617        member_id: int,
1618        item_id: int,
1619        membership_type: enums.MembershipType | int,
1620        components: list[enums.ComponentType],
1621    ) -> typedefs.JSONObject:
1622        collector = _collect_components(components)
1623
1624        resp = await self._request(
1625            RequestMethod.GET,
1626            f"Destiny2/{int(membership_type)}/Profile/{member_id}/Item/{item_id}/?components={collector}",
1627        )
1628        assert isinstance(resp, dict)
1629        return resp
1630
1631    async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> typedefs.JSONObject:
1632        resp = await self._request(
1633            RequestMethod.GET, f"Destiny2/Clan/{clan_id}/WeeklyRewardState/"
1634        )
1635        assert isinstance(resp, dict)
1636        return resp
1637
1638    async def fetch_available_locales(self) -> typedefs.JSONObject:
1639        resp = await self._request(
1640            RequestMethod.GET, "Destiny2/Manifest/DestinyLocaleDefinition/"
1641        )
1642        assert isinstance(resp, dict)
1643        return resp
1644
1645    async def fetch_common_settings(self) -> typedefs.JSONObject:
1646        resp = await self._request(RequestMethod.GET, "Settings")
1647        assert isinstance(resp, dict)
1648        return resp
1649
1650    async def fetch_user_systems_overrides(self) -> typedefs.JSONObject:
1651        resp = await self._request(RequestMethod.GET, "UserSystemOverrides")
1652        assert isinstance(resp, dict)
1653        return resp
1654
1655    async def fetch_global_alerts(
1656        self, *, include_streaming: bool = False
1657    ) -> typedefs.JSONArray:
1658        resp = await self._request(
1659            RequestMethod.GET, f"GlobalAlerts/?includestreaming={include_streaming}"
1660        )
1661        assert isinstance(resp, list)
1662        return resp
1663
1664    async def awainitialize_request(
1665        self,
1666        access_token: str,
1667        type: typing.Literal[0, 1],
1668        membership_type: enums.MembershipType | int,
1669        /,
1670        *,
1671        affected_item_id: int | None = None,
1672        character_id: int | None = None,
1673    ) -> typedefs.JSONObject:
1674        body = {"type": type, "membershipType": int(membership_type)}
1675
1676        if affected_item_id is not None:
1677            body["affectedItemId"] = affected_item_id
1678
1679        if character_id is not None:
1680            body["characterId"] = character_id
1681
1682        resp = await self._request(
1683            RequestMethod.POST, "Destiny2/Awa/Initialize", json=body, auth=access_token
1684        )
1685        assert isinstance(resp, dict)
1686        return resp
1687
1688    async def awaget_action_token(
1689        self, access_token: str, correlation_id: str, /
1690    ) -> typedefs.JSONObject:
1691        resp = await self._request(
1692            RequestMethod.POST,
1693            f"Destiny2/Awa/GetActionToken/{correlation_id}",
1694            auth=access_token,
1695        )
1696        assert isinstance(resp, dict)
1697        return resp
1698
1699    async def awa_provide_authorization_result(
1700        self,
1701        access_token: str,
1702        selection: int,
1703        correlation_id: str,
1704        nonce: collections.MutableSequence[str | bytes],
1705    ) -> int:
1706        body = {"selection": selection, "correlationId": correlation_id, "nonce": nonce}
1707
1708        resp = await self._request(
1709            RequestMethod.POST,
1710            "Destiny2/Awa/AwaProvideAuthorizationResult",
1711            json=body,
1712            auth=access_token,
1713        )
1714        assert isinstance(resp, int)
1715        return resp
1716
1717    async def fetch_vendors(
1718        self,
1719        access_token: str,
1720        character_id: int,
1721        membership_id: int,
1722        membership_type: enums.MembershipType | int,
1723        /,
1724        components: list[enums.ComponentType],
1725        filter: int | None = None,
1726    ) -> typedefs.JSONObject:
1727        components_ = _collect_components(components)
1728        route = (
1729            f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1730            f"/Character/{character_id}/Vendors/?components={components_}"
1731        )
1732
1733        if filter is not None:
1734            route = route + f"&filter={filter}"
1735
1736        resp = await self._request(
1737            RequestMethod.GET,
1738            route,
1739            auth=access_token,
1740        )
1741        assert isinstance(resp, dict)
1742        return resp
1743
1744    async def fetch_vendor(
1745        self,
1746        access_token: str,
1747        character_id: int,
1748        membership_id: int,
1749        membership_type: enums.MembershipType | int,
1750        vendor_hash: int,
1751        /,
1752        components: list[enums.ComponentType],
1753    ) -> typedefs.JSONObject:
1754        components_ = _collect_components(components)
1755        resp = await self._request(
1756            RequestMethod.GET,
1757            (
1758                f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1759                f"/Character/{character_id}/Vendors/{vendor_hash}/?components={components_}"
1760            ),
1761            auth=access_token,
1762        )
1763        assert isinstance(resp, dict)
1764        return resp
1765
1766    async def fetch_application_api_usage(
1767        self,
1768        access_token: str,
1769        application_id: int,
1770        /,
1771        *,
1772        start: datetime.datetime | None = None,
1773        end: datetime.datetime | None = None,
1774    ) -> typedefs.JSONObject:
1775        end_date, start_date = time.parse_date_range(end, start)
1776        resp = await self._request(
1777            RequestMethod.GET,
1778            f"App/ApiUsage/{application_id}/?end={end_date}&start={start_date}",
1779            auth=access_token,
1780        )
1781        assert isinstance(resp, dict)
1782        return resp
1783
1784    async def fetch_bungie_applications(self) -> typedefs.JSONArray:
1785        resp = await self._request(RequestMethod.GET, "App/FirstParty")
1786        assert isinstance(resp, list)
1787        return resp
1788
1789    async def fetch_content_type(self, type: str, /) -> typedefs.JSONObject:
1790        resp = await self._request(RequestMethod.GET, f"Content/GetContentType/{type}/")
1791        assert isinstance(resp, dict)
1792        return resp
1793
1794    async def fetch_content_by_id(
1795        self, id: int, locale: str, /, *, head: bool = False
1796    ) -> typedefs.JSONObject:
1797        resp = await self._request(
1798            RequestMethod.GET,
1799            f"Content/GetContentById/{id}/{locale}/",
1800            json={"head": head},
1801        )
1802        assert isinstance(resp, dict)
1803        return resp
1804
1805    async def fetch_content_by_tag_and_type(
1806        self, locale: str, tag: str, type: str, *, head: bool = False
1807    ) -> typedefs.JSONObject:
1808        resp = await self._request(
1809            RequestMethod.GET,
1810            f"Content/GetContentByTagAndType/{tag}/{type}/{locale}/",
1811            json={"head": head},
1812        )
1813        assert isinstance(resp, dict)
1814        return resp
1815
1816    async def search_content_with_text(
1817        self,
1818        locale: str,
1819        /,
1820        content_type: str,
1821        search_text: str,
1822        tag: str,
1823        *,
1824        page: int | None = None,
1825        source: str | None = None,
1826    ) -> typedefs.JSONObject:
1827        body: typedefs.JSONObject = {
1828            "locale": locale,
1829            "currentpage": page or 1,
1830            "ctype": content_type,
1831            "searchtxt": search_text,
1832            "ctype": content_type,
1833            "searchtext": search_text,
1834            "tag": tag,
1835            "source": source,
1836        }
1837
1838        resp = await self._request(RequestMethod.GET, "Content/Search", params=body)
1839        assert isinstance(resp, dict)
1840        return resp
1841
1842    async def search_content_by_tag_and_type(
1843        self,
1844        locale: str,
1845        tag: str,
1846        type: str,
1847        *,
1848        page: int | None = None,
1849    ) -> typedefs.JSONObject:
1850        body: typedefs.JSONObject = {"currentpage": page or 1}
1851
1852        resp = await self._request(
1853            RequestMethod.GET,
1854            f"Content/SearchContentByTagAndType/{tag}/{type}/{locale}/",
1855            params=body,
1856        )
1857        assert isinstance(resp, dict)
1858        return resp
1859
1860    async def search_help_articles(
1861        self, text: str, size: str, /
1862    ) -> typedefs.JSONObject:
1863        resp = await self._request(
1864            RequestMethod.GET, f"Content/SearchHelpArticles/{text}/{size}/"
1865        )
1866        assert isinstance(resp, dict)
1867        return resp
1868
1869    async def fetch_topics_page(
1870        self,
1871        category_filter: int,
1872        group: int,
1873        date_filter: int,
1874        sort: str | bytes,
1875        *,
1876        page: int | None = None,
1877        locales: collections.Iterable[str] | None = None,
1878        tag_filter: str | None = None,
1879    ) -> typedefs.JSONObject:
1880        params = {
1881            "locales": ",".join(locales) if locales is not None else "en",
1882        }
1883        if tag_filter:
1884            params["tagstring"] = tag_filter
1885
1886        resp = await self._request(
1887            RequestMethod.GET,
1888            f"Forum/GetTopicsPaged/{page or 0}/0/{group}/{sort!s}/{date_filter}/{category_filter}/",
1889            params=params,
1890        )
1891        assert isinstance(resp, dict)
1892        return resp
1893
1894    async def fetch_core_topics_page(
1895        self,
1896        category_filter: int,
1897        date_filter: int,
1898        sort: str | bytes,
1899        *,
1900        page: int | None = None,
1901        locales: collections.Iterable[str] | None = None,
1902    ) -> typedefs.JSONObject:
1903        resp = await self._request(
1904            RequestMethod.GET,
1905            f"Forum/GetCoreTopicsPaged/{page or 0}"
1906            f"/{sort!s}/{date_filter}/{category_filter}/?locales={','.join(locales) if locales else 'en'}",
1907        )
1908        assert isinstance(resp, dict)
1909        return resp
1910
1911    async def fetch_posts_threaded_page(
1912        self,
1913        parent_post: bool,
1914        page: int,
1915        page_size: int,
1916        parent_post_id: int,
1917        reply_size: int,
1918        root_thread_mode: bool,
1919        sort_mode: int,
1920        show_banned: str | None = None,
1921    ) -> typedefs.JSONObject:
1922        resp = await self._request(
1923            RequestMethod.GET,
1924            f"Forum/GetPostsThreadedPaged/{parent_post}/{page}/"
1925            f"{page_size}/{reply_size}/{parent_post_id}/{root_thread_mode}/{sort_mode}/",
1926            json={"showbanned": show_banned},
1927        )
1928        assert isinstance(resp, dict)
1929        return resp
1930
1931    async def fetch_posts_threaded_page_from_child(
1932        self,
1933        child_id: bool,
1934        page: int,
1935        page_size: int,
1936        reply_size: int,
1937        root_thread_mode: bool,
1938        sort_mode: int,
1939        show_banned: str | None = None,
1940    ) -> typedefs.JSONObject:
1941        resp = await self._request(
1942            RequestMethod.GET,
1943            f"Forum/GetPostsThreadedPagedFromChild/{child_id}/"
1944            f"{page}/{page_size}/{reply_size}/{root_thread_mode}/{sort_mode}/",
1945            json={"showbanned": show_banned},
1946        )
1947        assert isinstance(resp, dict)
1948        return resp
1949
1950    async def fetch_post_and_parent(
1951        self, child_id: int, /, *, show_banned: str | None = None
1952    ) -> typedefs.JSONObject:
1953        resp = await self._request(
1954            RequestMethod.GET,
1955            f"Forum/GetPostAndParent/{child_id}/",
1956            json={"showbanned": show_banned},
1957        )
1958        assert isinstance(resp, dict)
1959        return resp
1960
1961    async def fetch_posts_and_parent_awaiting(
1962        self, child_id: int, /, *, show_banned: str | None = None
1963    ) -> typedefs.JSONObject:
1964        resp = await self._request(
1965            RequestMethod.GET,
1966            f"Forum/GetPostAndParentAwaitingApproval/{child_id}/",
1967            json={"showbanned": show_banned},
1968        )
1969        assert isinstance(resp, dict)
1970        return resp
1971
1972    async def fetch_topic_for_content(self, content_id: int, /) -> int:
1973        resp = await self._request(
1974            RequestMethod.GET, f"Forum/GetTopicForContent/{content_id}/"
1975        )
1976        assert isinstance(resp, int)
1977        return resp
1978
1979    async def fetch_forum_tag_suggestions(
1980        self, partial_tag: str, /
1981    ) -> typedefs.JSONObject:
1982        resp = await self._request(
1983            RequestMethod.GET,
1984            "Forum/GetForumTagSuggestions/",
1985            json={"partialtag": partial_tag},
1986        )
1987        assert isinstance(resp, dict)
1988        return resp
1989
1990    async def fetch_poll(self, topic_id: int, /) -> typedefs.JSONObject:
1991        resp = await self._request(RequestMethod.GET, f"Forum/Poll/{topic_id}/")
1992        assert isinstance(resp, dict)
1993        return resp
1994
1995    async def fetch_recruitment_thread_summaries(self) -> typedefs.JSONArray:
1996        resp = await self._request(RequestMethod.POST, "Forum/Recruit/Summaries/")
1997        assert isinstance(resp, list)
1998        return resp
1999
2000    async def fetch_recommended_groups(
2001        self,
2002        access_token: str,
2003        /,
2004        *,
2005        date_range: int = 0,
2006        group_type: enums.GroupType | int = enums.GroupType.CLAN,
2007    ) -> typedefs.JSONArray:
2008        resp = await self._request(
2009            RequestMethod.POST,
2010            f"GroupV2/Recommended/{int(group_type)}/{date_range}/",
2011            auth=access_token,
2012        )
2013        assert isinstance(resp, list)
2014        return resp
2015
2016    async def fetch_available_avatars(self) -> collections.Mapping[str, int]:
2017        resp = await self._request(RequestMethod.GET, "GroupV2/GetAvailableAvatars/")
2018        assert isinstance(resp, dict)
2019        return resp
2020
2021    async def fetch_user_clan_invite_setting(
2022        self,
2023        access_token: str,
2024        /,
2025        membership_type: enums.MembershipType | int,
2026    ) -> bool:
2027        resp = await self._request(
2028            RequestMethod.GET,
2029            f"GroupV2/GetUserClanInviteSetting/{int(membership_type)}/",
2030            auth=access_token,
2031        )
2032        assert isinstance(resp, bool)
2033        return resp
2034
2035    async def fetch_banned_group_members(
2036        self, access_token: str, group_id: int, /, *, page: int = 1
2037    ) -> typedefs.JSONObject:
2038        resp = await self._request(
2039            RequestMethod.GET,
2040            f"GroupV2/{group_id}/Banned/?currentpage={page}",
2041            auth=access_token,
2042        )
2043        assert isinstance(resp, dict)
2044        return resp
2045
2046    async def fetch_pending_group_memberships(
2047        self, access_token: str, group_id: int, /, *, current_page: int = 1
2048    ) -> typedefs.JSONObject:
2049        resp = await self._request(
2050            RequestMethod.GET,
2051            f"GroupV2/{group_id}/Members/Pending/?currentpage={current_page}",
2052            auth=access_token,
2053        )
2054        assert isinstance(resp, dict)
2055        return resp
2056
2057    async def fetch_invited_group_memberships(
2058        self, access_token: str, group_id: int, /, *, current_page: int = 1
2059    ) -> typedefs.JSONObject:
2060        resp = await self._request(
2061            RequestMethod.GET,
2062            f"GroupV2/{group_id}/Members/InvitedIndividuals/?currentpage={current_page}",
2063            auth=access_token,
2064        )
2065        assert isinstance(resp, dict)
2066        return resp
2067
2068    async def invite_member_to_group(
2069        self,
2070        access_token: str,
2071        /,
2072        group_id: int,
2073        membership_id: int,
2074        membership_type: enums.MembershipType | int,
2075        *,
2076        message: str | None = None,
2077    ) -> typedefs.JSONObject:
2078        resp = await self._request(
2079            RequestMethod.POST,
2080            f"GroupV2/{group_id}/Members/IndividualInvite/{int(membership_type)}/{membership_id}/",
2081            auth=access_token,
2082            json={"message": str(message)},
2083        )
2084        assert isinstance(resp, dict)
2085        return resp
2086
2087    async def cancel_group_member_invite(
2088        self,
2089        access_token: str,
2090        /,
2091        group_id: int,
2092        membership_id: int,
2093        membership_type: enums.MembershipType | int,
2094    ) -> typedefs.JSONObject:
2095        resp = await self._request(
2096            RequestMethod.POST,
2097            f"GroupV2/{group_id}/Members/IndividualInviteCancel/{int(membership_type)}/{membership_id}/",
2098            auth=access_token,
2099        )
2100        assert isinstance(resp, dict)
2101        return resp
2102
2103    async def fetch_historical_definition(self) -> typedefs.JSONObject:
2104        resp = await self._request(RequestMethod.GET, "Destiny2/Stats/Definition/")
2105        assert isinstance(resp, dict)
2106        return resp
2107
2108    async def fetch_historical_stats(
2109        self,
2110        character_id: int,
2111        membership_id: int,
2112        membership_type: enums.MembershipType | int,
2113        day_start: datetime.datetime,
2114        day_end: datetime.datetime,
2115        groups: list[enums.StatsGroupType | int],
2116        modes: collections.Sequence[enums.GameMode | int],
2117        *,
2118        period_type: enums.PeriodType = enums.PeriodType.ALL_TIME,
2119    ) -> typedefs.JSONObject:
2120        end, start = time.parse_date_range(day_end, day_start)
2121        resp = await self._request(
2122            RequestMethod.GET,
2123            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/",
2124            json={
2125                "dayend": end,
2126                "daystart": start,
2127                "groups": [str(int(group)) for group in groups],
2128                "modes": [str(int(mode)) for mode in modes],
2129                "periodType": int(period_type),
2130            },
2131        )
2132        assert isinstance(resp, dict)
2133        return resp
2134
2135    async def fetch_historical_stats_for_account(
2136        self,
2137        membership_id: int,
2138        membership_type: enums.MembershipType | int,
2139        groups: list[enums.StatsGroupType | int],
2140    ) -> typedefs.JSONObject:
2141        resp = await self._request(
2142            RequestMethod.GET,
2143            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Stats/",
2144            json={"groups": [str(int(group)) for group in groups]},
2145        )
2146        assert isinstance(resp, dict)
2147        return resp
2148
2149    async def fetch_aggregated_activity_stats(
2150        self,
2151        character_id: int,
2152        membership_id: int,
2153        membership_type: enums.MembershipType | int,
2154        /,
2155    ) -> typedefs.JSONObject:
2156        resp = await self._request(
2157            RequestMethod.GET,
2158            f"Destiny2/{int(membership_type)}/Account/{membership_id}/"
2159            f"Character/{character_id}/Stats/AggregateActivityStats/",
2160        )
2161        assert isinstance(resp, dict)
2162        return resp
2163
2164    async def equip_loadout(
2165        self,
2166        access_token: str,
2167        /,
2168        loadout_index: int,
2169        character_id: int,
2170        membership_type: enums.MembershipType | int,
2171    ) -> None:
2172        response = await self._request(
2173            RequestMethod.POST,
2174            "Destiny2/Actions/Loadouts/EquipLoadout/",
2175            json={
2176                "loadoutIndex": loadout_index,
2177                "characterId": character_id,
2178                "membership_type": int(membership_type),
2179            },
2180            auth=access_token,
2181        )
2182        assert isinstance(response, int)
2183
2184    async def snapshot_loadout(
2185        self,
2186        access_token: str,
2187        /,
2188        loadout_index: int,
2189        character_id: int,
2190        membership_type: enums.MembershipType | int,
2191        *,
2192        color_hash: int | None = None,
2193        icon_hash: int | None = None,
2194        name_hash: int | None = None,
2195    ) -> None:
2196        response = await self._request(
2197            RequestMethod.POST,
2198            "Destiny2/Actions/Loadouts/SnapshotLoadout/",
2199            auth=access_token,
2200            json={
2201                "colorHash": color_hash,
2202                "iconHash": icon_hash,
2203                "nameHash": name_hash,
2204                "loadoutIndex": loadout_index,
2205                "characterId": character_id,
2206                "membershipType": int(membership_type),
2207            },
2208        )
2209        assert isinstance(response, int)
2210
2211    async def update_loadout(
2212        self,
2213        access_token: str,
2214        /,
2215        loadout_index: int,
2216        character_id: int,
2217        membership_type: enums.MembershipType | int,
2218        *,
2219        color_hash: int | None = None,
2220        icon_hash: int | None = None,
2221        name_hash: int | None = None,
2222    ) -> None:
2223        response = await self._request(
2224            RequestMethod.POST,
2225            "Destiny2/Actions/Loadouts/UpdateLoadoutIdentifiers/",
2226            auth=access_token,
2227            json={
2228                "colorHash": color_hash,
2229                "iconHash": icon_hash,
2230                "nameHash": name_hash,
2231                "loadoutIndex": loadout_index,
2232                "characterId": character_id,
2233                "membershipType": int(membership_type),
2234            },
2235        )
2236        assert isinstance(response, int)
2237
2238    async def clear_loadout(
2239        self,
2240        access_token: str,
2241        /,
2242        loadout_index: int,
2243        character_id: int,
2244        membership_type: enums.MembershipType | int,
2245    ) -> None:
2246        response = await self._request(
2247            RequestMethod.POST,
2248            "Destiny2/Actions/Loadouts/ClearLoadout/",
2249            json={
2250                "loadoutIndex": loadout_index,
2251                "characterId": character_id,
2252                "membership_type": int(membership_type),
2253            },
2254            auth=access_token,
2255        )
2256        assert isinstance(response, int)

A RESTful client implementation for Bungie's API.

This client is designed to only make HTTP requests and return JSON objects to provide RESTful functionality.

This client is the core for aiobungie.Client which deserialize those returned JSON objects using the factory into Pythonic data classes objects which provide Python functionality.

Example
import aiobungie

client = aiobungie.RESTClient("TOKEN")
async with client:
    response = await client.fetch_clan_members(4389205)
    for member in response['results']:
        for key, value in member['destinyUserInfo'].items():
            print(key, value)
Parameters
  • token (str): A valid application token from Bungie's developer portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • client_secret (str | None): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (int | None): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
  • enable_debugging (bool | str): Whether to enable logging responses or not.
Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information.
  • "TRACE" | TRACE: This will log the response headers along with the minimal information.
RESTClient( token: str, /, *, client_secret: str | None = None, client_id: int | None = None, client_session: aiohttp.client.ClientSession | None = None, dumps: collections.abc.Callable[[collections.abc.Mapping[str, typing.Any] | collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]], bytes] = <function dumps>, loads: collections.abc.Callable[[str | bytes], collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]] | collections.abc.Mapping[str, typing.Any]] = <function loads>, max_retries: int = 4, enable_debugging: Union[Literal['TRACE'], bool, int] = False)
364    def __init__(
365        self,
366        token: str,
367        /,
368        *,
369        client_secret: str | None = None,
370        client_id: int | None = None,
371        client_session: aiohttp.ClientSession | None = None,
372        dumps: typedefs.Dumps = helpers.dumps,
373        loads: typedefs.Loads = helpers.loads,
374        max_retries: int = 4,
375        enable_debugging: typing.Literal["TRACE"] | bool | int = False,
376    ) -> None:
377        self._session = client_session
378        self._lock: asyncio.Lock | None = None
379        self._client_secret = client_secret
380        self._client_id = client_id
381        self._token: str = token
382        self._max_retries = max_retries
383        self._dumps = dumps
384        self._loads = loads
385        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
386
387        self._set_debug_level(enable_debugging)
client_id: int | None

Return the client id of this REST client if provided, Otherwise None.

metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

A mutable mapping storage for the user's needs.

This mapping is useful for storing any kind of data that the user may need.

Example
import aiobungie

client = aiobungie.RESTClient(…)

async with client:
    # Fetch auth tokens and store them
    client.metadata["tokens"] = await client.fetch_access_token("code")

# Some other time.
async with client:
    # Retrieve the tokens
    tokens: aiobungie.OAuth2Response = client.metadata["tokens"]

    # Use them to fetch your user.
    user = await client.fetch_current_user_memberships(tokens.access_token)
is_alive: bool

Returns True if the REST client is alive and False otherwise.

@typing.final
async def close(self) -> None:
401    @typing.final
402    async def close(self) -> None:
403        if self._session is None:
404            raise RuntimeError("REST client is not running.")
405
406        await self._session.close()
407        self._session = None

Close this REST client session if it was acquired.

This method is automatically called when using async with contextmanager.

Raises
  • RuntimeError: If the client is already closed.
@typing.final
def open(self) -> None:
409    @typing.final
410    def open(self) -> None:
411        """Open a new client session. This is called internally with contextmanager usage."""
412        if self._session:
413            raise RuntimeError("Cannot open REST client when it's already open.")
414
415        self._session = aiohttp.ClientSession(
416            connector=aiohttp.TCPConnector(),
417            connector_owner=True,
418            raise_for_status=False,
419            timeout=aiohttp.ClientTimeout(total=30.0),
420        )

Open a new client session. This is called internally with contextmanager usage.

@typing.final
def enable_debugging( self, level: Union[Literal['TRACE'], bool, int] = False, file: pathlib.Path | str | None = None, /) -> None:
422    @typing.final
423    def enable_debugging(
424        self,
425        level: typing.Literal["TRACE"] | bool | int = False,
426        file: pathlib.Path | str | None = None,
427        /,
428    ) -> None:
429        self._set_debug_level(level, file)

Enables debugging for the REST calls.

Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information.
  • "TRACE" | TRACE: This will log the response headers along with the minimal information.
Parameters
  • level (str | bool | int): The level of debugging to enable.
  • file (pathlib.Path | str | None): The file path to write the debug logs to. If provided.
@typing.final
async def static_request( self, method: RequestMethod | str, path: str, *, auth: str | None = None, json: collections.abc.Mapping[str, typing.Any] | None = None) -> collections.abc.Mapping[str, typing.Any] | collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]] | bytes | str | int | bool | None:
431    @typing.final
432    async def static_request(
433        self,
434        method: RequestMethod | str,
435        path: str,
436        *,
437        auth: str | None = None,
438        json: collections.Mapping[str, typing.Any] | None = None,
439    ) -> typedefs.JSONIsh:
440        return await self._request(method, path, auth=auth, json=json)

Perform an HTTP request given a valid Bungie endpoint.

Parameters
  • method (RequestMethod | str): The request method, This may be GET, POST, PUT, etc.
  • path (str): The Bungie endpoint or path. A path must look something like this Destiny2/3/Profile/46111239123/...
Other Parameters
  • auth (str | None): An optional bearer token for methods that requires OAuth2 Authorization header.
  • json (MutableMapping[str, typing.Any] | None): An optional JSON mapping to include in the request.
Returns
  • aiobungie.typedefs.JSONIsh: The response payload.
@typing.final
def build_oauth2_url(self, client_id: int | None = None) -> aiobungie.builders.OAuthURL | None:
442    @typing.final
443    def build_oauth2_url(
444        self, client_id: int | None = None
445    ) -> builders.OAuthURL | None:
446        client_id = client_id or self._client_id
447        if client_id is None:
448            return None
449
450        return builders.OAuthURL(client_id=client_id)

Builds an OAuth2 URL using the provided user REST/Base client secret/id.

You can't get the complete string URL by using .compile() method.

Parameters
  • client_id (int | None): An optional client id to provide, If left None it will roll back to the id passed to the RESTClient, If both is None this method will return None.
Returns
async def fetch_oauth2_tokens(self, code: str, /) -> aiobungie.builders.OAuth2Response:
641    async def fetch_oauth2_tokens(self, code: str, /) -> builders.OAuth2Response:
642        if not isinstance(self._client_secret, (str, int)):
643            raise TypeError(
644                "Expected (str, int) for client secret "
645                f"but got {type(self._client_secret).__name__}"  # type: ignore
646            )
647
648        headers = {
649            "client_secret": self._client_secret,
650        }
651
652        data = (
653            f"grant_type=authorization_code&code={code}"
654            f"&client_id={self._client_id}&client_secret={self._client_secret}"
655        )
656
657        response = await self._request(
658            RequestMethod.POST, "", headers=headers, json=data, oauth2=True
659        )
660        assert isinstance(response, dict)
661        return builders.OAuth2Response.build_response(response)

Makes a POST request and fetch the OAuth2 access_token and refresh token.

Parameters
  • code (str): The Authorization code received from the authorization endpoint found in the URL parameters.
Returns
Raises
async def refresh_access_token(self, refresh_token: str, /) -> aiobungie.builders.OAuth2Response:
663    async def refresh_access_token(
664        self, refresh_token: str, /
665    ) -> builders.OAuth2Response:
666        if not isinstance(self._client_secret, (int, str)):
667            raise TypeError(
668                f"Expected (str, int) for client secret but got {type(self._client_secret).__name__}"  # type: ignore
669            )
670
671        data = {
672            "grant_type": "refresh_token",
673            "refresh_token": refresh_token,
674            "client_id": self._client_id,
675            "client_secret": self._client_secret,
676            "Content-Type": "application/x-www-form-urlencoded",
677        }
678
679        response = await self._request(RequestMethod.POST, "", json=data, oauth2=True)
680        assert isinstance(response, dict)
681        return builders.OAuth2Response.build_response(response)

Refresh OAuth2 access token given its refresh token.

Parameters
  • refresh_token (str): The refresh token.
Returns
async def fetch_bungie_user(self, id: int) -> collections.abc.Mapping[str, typing.Any]:
683    async def fetch_bungie_user(self, id: int) -> typedefs.JSONObject:
684        resp = await self._request(
685            RequestMethod.GET, f"User/GetBungieNetUserById/{id}/"
686        )
687        assert isinstance(resp, dict)
688        return resp

Fetch a Bungie user by their id.

Parameters
  • id (int): The user id.
Returns
Raises
async def fetch_user_themes( self) -> collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]:
690    async def fetch_user_themes(self) -> typedefs.JSONArray:
691        resp = await self._request(RequestMethod.GET, "User/GetAvailableThemes/")
692        assert isinstance(resp, list)
693        return resp

Fetch all available user themes.

Returns
async def fetch_membership_from_id( self, id: int, type: MembershipType | int = <MembershipType.NONE: 0>, /) -> collections.abc.Mapping[str, typing.Any]:
695    async def fetch_membership_from_id(
696        self,
697        id: int,
698        type: enums.MembershipType | int = enums.MembershipType.NONE,
699        /,
700    ) -> typedefs.JSONObject:
701        resp = await self._request(
702            RequestMethod.GET, f"User/GetMembershipsById/{id}/{int(type)}"
703        )
704        assert isinstance(resp, dict)
705        return resp

Fetch Bungie user's memberships from their id.

Parameters
  • id (int): The user's id.
  • type (aiobungie.aiobungie.MembershipType | int): The user's membership type.
Returns
Raises
async def fetch_membership( self, name: str, code: int, type: MembershipType | int = <MembershipType.ALL: -1>, /) -> collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]:
707    async def fetch_membership(
708        self,
709        name: str,
710        code: int,
711        type: enums.MembershipType | int = enums.MembershipType.ALL,
712        /,
713    ) -> typedefs.JSONArray:
714        resp = await self._request(
715            RequestMethod.POST,
716            f"Destiny2/SearchDestinyPlayerByBungieName/{int(type)}",
717            json={"displayName": name, "displayNameCode": code},
718        )
719        assert isinstance(resp, list)
720        return resp

Fetch a Destiny 2 Player.

Parameters
  • name (str): The unique Bungie player name.
  • code (int): The unique Bungie display name code.
  • type (aiobungie.aiobungie.MembershipType | int): The player's membership type, e,g. XBOX, STEAM, PSN
Returns
Raises
async def search_users(self, name: str, /) -> collections.abc.Mapping[str, typing.Any]:
722    async def search_users(self, name: str, /) -> typedefs.JSONObject:
723        resp = await self._request(
724            RequestMethod.POST,
725            "User/Search/GlobalName/0",
726            json={"displayNamePrefix": name},
727        )
728        assert isinstance(resp, dict)
729        return resp

Search for users by their global name and return all users who share this name.

Parameters
  • name (str): The user name.
Returns
Raises
async def fetch_clan_from_id( self, id: int, /, access_token: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
731    async def fetch_clan_from_id(
732        self, id: int, /, access_token: str | None = None
733    ) -> typedefs.JSONObject:
734        resp = await self._request(
735            RequestMethod.GET, f"GroupV2/{id}", auth=access_token
736        )
737        assert isinstance(resp, dict)
738        return resp

Fetch a Bungie Clan by its id.

Parameters
  • id (int): The clan id.
Other Parameters
  • access_token (str | None): An optional access token to make the request with.

    If the token was bound to a member of the clan, This field aiobungie.crates.Clan.current_user_membership will be available and will return the membership of the user who made this request.

Returns
Raises
async def fetch_clan( self, name: str, /, access_token: str | None = None, *, type: GroupType | int = <GroupType.CLAN: 1>) -> collections.abc.Mapping[str, typing.Any]:
740    async def fetch_clan(
741        self,
742        name: str,
743        /,
744        access_token: str | None = None,
745        *,
746        type: enums.GroupType | int = enums.GroupType.CLAN,
747    ) -> typedefs.JSONObject:
748        resp = await self._request(
749            RequestMethod.GET, f"GroupV2/Name/{name}/{int(type)}", auth=access_token
750        )
751        assert isinstance(resp, dict)
752        return resp

Fetch a Clan by its name. This method will return the first clan found with given name name.

Parameters
  • name (str): The clan name.
Other Parameters
  • access_token (str | None): An optional access token to make the request with.

    If the token was bound to a member of the clan, This field aiobungie.crates.Clan.current_user_membership will be available and will return the membership of the user who made this request.

  • type (aiobungie.aiobungie.GroupType | int): The group type, Default is one.
Returns
Raises
async def fetch_clan_admins(self, clan_id: int, /) -> collections.abc.Mapping[str, typing.Any]:
754    async def fetch_clan_admins(self, clan_id: int, /) -> typedefs.JSONObject:
755        resp = await self._request(
756            RequestMethod.GET, f"GroupV2/{clan_id}/AdminsAndFounder/"
757        )
758        assert isinstance(resp, dict)
759        return resp

Fetch the admins and founder members of the clan.

Parameters
  • clan_id (int): The clan id.
Returns
Raises
async def fetch_clan_conversations( self, clan_id: int, /) -> collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]:
761    async def fetch_clan_conversations(self, clan_id: int, /) -> typedefs.JSONArray:
762        resp = await self._request(
763            RequestMethod.GET, f"GroupV2/{clan_id}/OptionalConversations/"
764        )
765        assert isinstance(resp, list)
766        return resp

Fetch a clan's conversations.

Parameters
  • clan_id (int): The clan's id.
Returns
async def fetch_application(self, appid: int, /) -> collections.abc.Mapping[str, typing.Any]:
768    async def fetch_application(self, appid: int, /) -> typedefs.JSONObject:
769        resp = await self._request(RequestMethod.GET, f"App/Application/{appid}")
770        assert isinstance(resp, dict)
771        return resp

Fetch a Bungie Application.

Parameters
  • appid (int): The application id.
Returns
async def fetch_character( self, member_id: int, membership_type: MembershipType | int, character_id: int, components: list[ComponentType], auth: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
773    async def fetch_character(
774        self,
775        member_id: int,
776        membership_type: enums.MembershipType | int,
777        character_id: int,
778        components: list[enums.ComponentType],
779        auth: str | None = None,
780    ) -> typedefs.JSONObject:
781        collector = _collect_components(components)
782        response = await self._request(
783            RequestMethod.GET,
784            f"Destiny2/{int(membership_type)}/Profile/{member_id}/"
785            f"Character/{character_id}/?components={collector}",
786            auth=auth,
787        )
788        assert isinstance(response, dict)
789        return response

Fetch a Destiny 2 player's characters.

Parameters
  • member_id (int): A valid bungie member id.
  • membership_type (aiobungie.aiobungie.internal.enums.MembershipType | int): The member's membership type.
  • character_id (int): The character id to return.
  • components (list[aiobungie.ComponentType]): A list of character components to collect and return.
Other Parameters
  • auth (str | None): A bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
Raises
async def fetch_activities( self, member_id: int, character_id: int, mode: GameMode | int, membership_type: MembershipType | int = <MembershipType.ALL: -1>, *, page: int = 0, limit: int = 1) -> collections.abc.Mapping[str, typing.Any]:
791    async def fetch_activities(
792        self,
793        member_id: int,
794        character_id: int,
795        mode: enums.GameMode | int,
796        membership_type: enums.MembershipType | int = enums.MembershipType.ALL,
797        *,
798        page: int = 0,
799        limit: int = 1,
800    ) -> typedefs.JSONObject:
801        resp = await self._request(
802            RequestMethod.GET,
803            f"Destiny2/{int(membership_type)}/Account/"
804            f"{member_id}/Character/{character_id}/Stats/Activities"
805            f"/?mode={int(mode)}&count={limit}&page={page}",
806        )
807        assert isinstance(resp, dict)
808        return resp

Fetch a Destiny 2 activity for the specified user id and character.

Parameters
  • member_id (int): The user id that starts with 4611.
  • character_id (int): The id of the character to retrieve.
  • mode (aiobungie.aiobungie.GameMode | int): This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
Other Parameters
  • membership_type (aiobungie.aiobungie.internal.enums.MembershipType | int): The Member ship type, if nothing was passed than it will return all.
  • page (int): The page number. Default to 1
  • limit (int): Limit the returned result. Default to 1
Returns
Raises
async def fetch_vendor_sales(self) -> collections.abc.Mapping[str, typing.Any]:
810    async def fetch_vendor_sales(self) -> typedefs.JSONObject:
811        resp = await self._request(
812            RequestMethod.GET,
813            f"Destiny2/Vendors/?components={int(enums.ComponentType.VENDOR_SALES)}",
814        )
815        assert isinstance(resp, dict)
816        return resp
async def fetch_profile( self, membership_id: int, type: MembershipType | int, components: list[ComponentType], auth: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
818    async def fetch_profile(
819        self,
820        membership_id: int,
821        type: enums.MembershipType | int,
822        components: list[enums.ComponentType],
823        auth: str | None = None,
824    ) -> typedefs.JSONObject:
825        collector = _collect_components(components)
826        response = await self._request(
827            RequestMethod.GET,
828            f"Destiny2/{int(type)}/Profile/{membership_id}/?components={collector}",
829            auth=auth,
830        )
831        assert isinstance(response, dict)
832        return response

Fetch a bungie profile.

Parameters
  • membership_id (int): The member's id.
  • type (aiobungie.aiobungie.MembershipType | int): A valid membership type.
  • components (list[aiobungie.ComponentType]): A list of profile components to collect and return.
Other Parameters
  • auth (str | None): A bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
Raises
async def fetch_entity(self, type: str, hash: int) -> collections.abc.Mapping[str, typing.Any]:
834    async def fetch_entity(self, type: str, hash: int) -> typedefs.JSONObject:
835        response = await self._request(
836            RequestMethod.GET, route=f"Destiny2/Manifest/{type}/{hash}"
837        )
838        assert isinstance(response, dict)
839        return response

Fetch a Destiny definition item given its type and hash.

Parameters
  • type (str): Entity's type definition.
  • hash (int): Entity's hash.
Returns
async def fetch_inventory_item(self, hash: int, /) -> collections.abc.Mapping[str, typing.Any]:
841    async def fetch_inventory_item(self, hash: int, /) -> typedefs.JSONObject:
842        resp = await self.fetch_entity("DestinyInventoryItemDefinition", hash)
843        assert isinstance(resp, dict)
844        return resp

Fetch a Destiny inventory item entity given a its hash.

Parameters
  • hash (int): Entity's hash.
Returns
async def fetch_objective_entity(self, hash: int, /) -> collections.abc.Mapping[str, typing.Any]:
846    async def fetch_objective_entity(self, hash: int, /) -> typedefs.JSONObject:
847        resp = await self.fetch_entity("DestinyObjectiveDefinition", hash)
848        assert isinstance(resp, dict)
849        return resp

Fetch a Destiny objective entity given a its hash.

Parameters
  • hash (int): objective's hash.
Returns
async def fetch_groups_for_member( self, member_id: int, member_type: MembershipType | int, /, *, filter: int = 0, group_type: GroupType | int = <GroupType.CLAN: 1>) -> collections.abc.Mapping[str, typing.Any]:
851    async def fetch_groups_for_member(
852        self,
853        member_id: int,
854        member_type: enums.MembershipType | int,
855        /,
856        *,
857        filter: int = 0,
858        group_type: enums.GroupType | int = enums.GroupType.CLAN,
859    ) -> typedefs.JSONObject:
860        resp = await self._request(
861            RequestMethod.GET,
862            f"GroupV2/User/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
863        )
864        assert isinstance(resp, dict)
865        return resp

Fetch the information about the groups for a member.

Parameters
  • member_id (int): The member's id
  • member_type (aiobungie.aiobungie.MembershipType | int): The member's membership type.
Other Parameters
  • filter (int): Filter apply to list of joined groups. This Default to 0
  • group_type (aiobungie.aiobungie.GroupType | int): The group's type. This is always set to aiobungie.GroupType.CLAN and should not be changed.
Returns
async def fetch_potential_groups_for_member( self, member_id: int, member_type: MembershipType | int, /, *, filter: int = 0, group_type: GroupType | int = <GroupType.CLAN: 1>) -> collections.abc.Mapping[str, typing.Any]:
867    async def fetch_potential_groups_for_member(
868        self,
869        member_id: int,
870        member_type: enums.MembershipType | int,
871        /,
872        *,
873        filter: int = 0,
874        group_type: enums.GroupType | int = enums.GroupType.CLAN,
875    ) -> typedefs.JSONObject:
876        resp = await self._request(
877            RequestMethod.GET,
878            f"GroupV2/User/Potential/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
879        )
880        assert isinstance(resp, dict)
881        return resp

Get information about the groups that a given member has applied to or been invited to.

Parameters
  • member_id (int): The member's id
  • member_type (aiobungie.aiobungie.MembershipType | int): The member's membership type.
Other Parameters
  • filter (int): Filter apply to list of joined groups. This Default to 0
  • group_type (aiobungie.aiobungie.GroupType | int): The group's type. This is always set to aiobungie.GroupType.CLAN and should not be changed.
Returns
async def fetch_clan_members( self, clan_id: int, /, *, name: str | None = None, type: MembershipType | int = <MembershipType.NONE: 0>) -> collections.abc.Mapping[str, typing.Any]:
883    async def fetch_clan_members(
884        self,
885        clan_id: int,
886        /,
887        *,
888        name: str | None = None,
889        type: enums.MembershipType | int = enums.MembershipType.NONE,
890    ) -> typedefs.JSONObject:
891        resp = await self._request(
892            RequestMethod.GET,
893            f"/GroupV2/{clan_id}/Members/?memberType={int(type)}&nameSearch={name if name else ''}&currentpage=1",
894        )
895        assert isinstance(resp, dict)
896        return resp

Fetch all Bungie Clan members.

Parameters
  • clan_id (int): The clans id
Other Parameters
  • name (str | None): If provided, Only players matching this name will be returned.
  • type (aiobungie.aiobungie.MembershipType | int): An optional clan member's membership type. Default is set to aiobungie.MembershipType.NONE Which returns the first matched clan member by their name.
Returns
Raises
async def fetch_hardlinked_credentials( self, credential: int, type: CredentialType | int = <CredentialType.STEAMID: 12>, /) -> collections.abc.Mapping[str, typing.Any]:
898    async def fetch_hardlinked_credentials(
899        self,
900        credential: int,
901        type: enums.CredentialType | int = enums.CredentialType.STEAMID,
902        /,
903    ) -> typedefs.JSONObject:
904        resp = await self._request(
905            RequestMethod.GET,
906            f"User/GetMembershipFromHardLinkedCredential/{int(type)}/{credential}/",
907        )
908        assert isinstance(resp, dict)
909        return resp

Gets any hard linked membership given a credential.

Only works for credentials that are public just aiobungie.CredentialType.STEAMID right now. Cross Save aware.

Parameters
  • credential (int): A valid SteamID64
  • type (aiobungie.aiobungie.CredentialType | int): The credential type. This must not be changed Since its only credential that works "currently"
Returns
async def fetch_user_credentials( self, access_token: str, membership_id: int, /) -> collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]:
911    async def fetch_user_credentials(
912        self, access_token: str, membership_id: int, /
913    ) -> typedefs.JSONArray:
914        resp = await self._request(
915            RequestMethod.GET,
916            f"User/GetCredentialTypesForTargetAccount/{membership_id}",
917            auth=access_token,
918        )
919        assert isinstance(resp, list)
920        return resp

Fetch an array of credential types attached to the requested account.

This method require OAuth2 Bearer access token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • membership_id (int): The id of the membership to return.
Returns
Raises
async def insert_socket_plug( self, action_token: str, /, instance_id: int, plug: aiobungie.builders.PlugSocketBuilder | collections.abc.Mapping[str, int], character_id: int, membership_type: MembershipType | int) -> collections.abc.Mapping[str, typing.Any]:
922    async def insert_socket_plug(
923        self,
924        action_token: str,
925        /,
926        instance_id: int,
927        plug: builders.PlugSocketBuilder | collections.Mapping[str, int],
928        character_id: int,
929        membership_type: enums.MembershipType | int,
930    ) -> typedefs.JSONObject:
931        if isinstance(plug, builders.PlugSocketBuilder):
932            plug = plug.collect()
933
934        body = {
935            "actionToken": action_token,
936            "itemInstanceId": instance_id,
937            "plug": plug,
938            "characterId": character_id,
939            "membershipType": int(membership_type),
940        }
941        resp = await self._request(
942            RequestMethod.POST, "Destiny2/Actions/Items/InsertSocketPlug", json=body
943        )
944        assert isinstance(resp, dict)
945        return resp

Insert a plug into a socketed item.

OAuth2: AdvancedWriteActions scope is required

Parameters
  • action_token (str): Action token provided by the AwaGetActionToken API call.
  • instance_id (int): The item instance id that's plug inserted.
  • plug (aiobungie.builders.PlugSocketBuilder | Mapping[str, int]): Either a PlugSocketBuilder object or a raw dict contains key, value for the plug entries.
Example
plug = (
    aiobungie.PlugSocketBuilder()
    .set_socket_array(0)
    .set_socket_index(0)
    .set_plug_item(3023847)
    .collect()
)
await insert_socket_plug_free(..., plug=plug)

character_id : int The character's id. membership_type : aiobungie.aiobungie.MembershipType | int The membership type.

Returns
Raises
async def insert_socket_plug_free( self, access_token: str, /, instance_id: int, plug: aiobungie.builders.PlugSocketBuilder | collections.abc.Mapping[str, int], character_id: int, membership_type: MembershipType | int) -> collections.abc.Mapping[str, typing.Any]:
947    async def insert_socket_plug_free(
948        self,
949        access_token: str,
950        /,
951        instance_id: int,
952        plug: builders.PlugSocketBuilder | collections.Mapping[str, int],
953        character_id: int,
954        membership_type: enums.MembershipType | int,
955    ) -> typedefs.JSONObject:
956        if isinstance(plug, builders.PlugSocketBuilder):
957            plug = plug.collect()
958
959        body = {
960            "itemInstanceId": instance_id,
961            "plug": plug,
962            "characterId": character_id,
963            "membershipType": int(membership_type),
964        }
965        resp = await self._request(
966            RequestMethod.POST,
967            "Destiny2/Actions/Items/InsertSocketPlugFree",
968            json=body,
969            auth=access_token,
970        )
971        assert isinstance(resp, dict)
972        return resp

Insert a plug into a socketed item. This doesn't require an Action token.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • instance_id (int): The item instance id that's plug inserted.
  • plug (aiobungie.builders.PlugSocketBuilder | Mapping[str, int]): Either a PlugSocketBuilder object or a raw dict contains key, value for the plug entries.
Example
plug = (
    aiobungie.PlugSocketBuilder()
    .set_socket_array(0)
    .set_socket_index(0)
    .set_plug_item(3023847)
    .collect()
)
await insert_socket_plug_free(..., plug=plug)

character_id : int The character's id. membership_type : aiobungie.aiobungie.MembershipType | int The membership type.

Returns
Raises
async def set_item_lock_state( self, access_token: str, state: bool, /, item_id: int, character_id: int, membership_type: MembershipType | int) -> int:
974    async def set_item_lock_state(
975        self,
976        access_token: str,
977        state: bool,
978        /,
979        item_id: int,
980        character_id: int,
981        membership_type: enums.MembershipType | int,
982    ) -> int:
983        body = {
984            "state": state,
985            "itemId": item_id,
986            "characterId": character_id,
987            "membershipType": int(membership_type),
988        }
989        response = await self._request(
990            RequestMethod.POST,
991            "Destiny2/Actions/Items/SetLockState",
992            json=body,
993            auth=access_token,
994        )
995        assert isinstance(response, int)
996        return response

Set the Lock State for an instanced item.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • state (bool): If True, The item will be locked, If False, The item will be unlocked.
  • item_id (int): The item id.
  • character_id (int): The character id.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The membership type for the associated account.
Returns
  • int: An integer represents whether the request was successful or failed.
Raises
async def set_quest_track_state( self, access_token: str, state: bool, /, item_id: int, character_id: int, membership_type: MembershipType | int) -> int:
 998    async def set_quest_track_state(
 999        self,
1000        access_token: str,
1001        state: bool,
1002        /,
1003        item_id: int,
1004        character_id: int,
1005        membership_type: enums.MembershipType | int,
1006    ) -> int:
1007        body = {
1008            "state": state,
1009            "itemId": item_id,
1010            "characterId": character_id,
1011            "membership_type": int(membership_type),
1012        }
1013        response = await self._request(
1014            RequestMethod.POST,
1015            "Destiny2/Actions/Items/SetTrackedState",
1016            json=body,
1017            auth=access_token,
1018        )
1019        assert isinstance(response, int)
1020        return response

Set the Tracking State for an instanced Quest or Bounty.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • state (bool): If True, The item will be locked, If False, The item will be unlocked.
  • item_id (int): The item id.
  • character_id (int): The character id.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The membership type for the associated account.
Returns
  • int: An integer represents whether the request was successful or failed.
Raises
async def fetch_manifest_path(self) -> collections.abc.Mapping[str, typing.Any]:
1022    async def fetch_manifest_path(self) -> typedefs.JSONObject:
1023        path = await self._request(RequestMethod.GET, "Destiny2/Manifest")
1024        assert isinstance(path, dict)
1025        return path

Fetch the manifest JSON paths.

Returns
  • typedefs.JSONObject: The manifest JSON paths.
async def read_manifest_bytes(self, language: str = 'en', /) -> bytes:
1027    async def read_manifest_bytes(self, language: str = "en", /) -> bytes:
1028        _ensure_manifest_language(language)
1029
1030        content = await self.fetch_manifest_path()
1031        resp = await self._request(
1032            RequestMethod.GET,
1033            content["mobileWorldContentPaths"][language],
1034            unwrapping="read",
1035            base=True,
1036        )
1037        assert isinstance(resp, bytes)
1038        return resp

Read raw manifest SQLite database bytes response.

This method can be used to write the bytes to zipped file and then extract it to get the manifest content.

Parameters
  • language (str): The manifest database language bytes to get.
Returns
  • bytes: The bytes to read and write the manifest database.
async def download_sqlite_manifest( self, language: str = 'en', name: str = 'manifest', path: pathlib.Path | str = '.', *, force: bool = False) -> pathlib.Path:
1040    async def download_sqlite_manifest(
1041        self,
1042        language: str = "en",
1043        name: str = "manifest",
1044        path: pathlib.Path | str = ".",
1045        *,
1046        force: bool = False,
1047    ) -> pathlib.Path:
1048        complete_path = _get_path(name, path, sql=True)
1049
1050        if complete_path.exists() and force:
1051            if force:
1052                _LOG.info(
1053                    f"Found manifest in {complete_path!s}. Forcing to Re-Download."
1054                )
1055                complete_path.unlink(missing_ok=True)
1056
1057                return await self.download_sqlite_manifest(
1058                    language, name, path, force=force
1059                )
1060
1061            else:
1062                raise FileExistsError(
1063                    "Manifest file already exists, "
1064                    "To force download, set the `force` parameter to `True`."
1065                )
1066
1067        _LOG.info(f"Downloading manifest. Location: {complete_path!s}")
1068        data_bytes = await self.read_manifest_bytes(language)
1069        await asyncio.get_running_loop().run_in_executor(
1070            None, _write_sqlite_bytes, data_bytes, path, name
1071        )
1072        return _get_path(name, path, sql=True)

Downloads the SQLite version of Destiny2's Manifest.

Example
manifest = await rest.download_sqlite_manifest()
with sqlite3.connect(manifest) as conn:
    ...
Parameters
  • language (str): The manifest language to download, Default is English.
  • path (str | pathlib.Path): The path to download this manifest. Example "/tmp/databases/", Default is the current directory.
  • name (str): The manifest database file name. Default is manifest
  • force (bool): Whether to force the download. Default is False. However if set to true the old file will get unlinked and a new one will begin to download.
Returns
  • pathlib.Path: A pathlib object of the .sqlite file.
Raises
  • FileNotFoundError: If the manifest file exists and force is False.
  • ValueError: If the provided language was not recognized.
async def download_json_manifest( self, file_name: str = 'manifest', path: str | pathlib.Path = '.', language: str = 'en') -> pathlib.Path:
1074    async def download_json_manifest(
1075        self,
1076        file_name: str = "manifest",
1077        path: str | pathlib.Path = ".",
1078        language: str = "en",
1079    ) -> pathlib.Path:
1080        _ensure_manifest_language(language)
1081
1082        _LOG.info(f"Downloading manifest JSON to {_get_path(file_name, path)!r}...")
1083
1084        content = await self.fetch_manifest_path()
1085        json_bytes = await self._request(
1086            RequestMethod.GET,
1087            content["jsonWorldContentPaths"][language],
1088            unwrapping="read",
1089            base=True,
1090        )
1091
1092        await asyncio.get_running_loop().run_in_executor(
1093            None, _write_json_bytes, json_bytes, file_name, path
1094        )
1095        _LOG.info("Finished downloading manifest JSON.")
1096        return _get_path(file_name, path)

Download the Bungie manifest json file.

Example
manifest = await rest.download_json_manifest()
with open(manifest, "r") as f:
    to_dict = json.loads(f.read())
    item_definitions = to_dict['DestinyInventoryItemDefinition']
Parameters
  • file_name (str): The file name to save the manifest json file. Default is manifest.
  • path (str | pathlib.Path): The path to save the manifest json file. Default is the current directory. Example "D:/"
  • language (str): The manifest database language bytes to get. Default is English.
Returns
  • pathlib.Path: The path of this JSON manifest.
async def fetch_manifest_version(self) -> str:
1098    async def fetch_manifest_version(self) -> str:
1099        # This is guaranteed str.
1100        return (await self.fetch_manifest_path())["version"]  # type: ignore[no-any-return]

Fetch the manifest version.

Returns
  • str: The manifest version.
async def fetch_linked_profiles( self, member_id: int, member_type: MembershipType | int, /, *, all: bool = False) -> collections.abc.Mapping[str, typing.Any]:
1102    async def fetch_linked_profiles(
1103        self,
1104        member_id: int,
1105        member_type: enums.MembershipType | int,
1106        /,
1107        *,
1108        all: bool = False,
1109    ) -> typedefs.JSONObject:
1110        resp = await self._request(
1111            RequestMethod.GET,
1112            f"Destiny2/{int(member_type)}/Profile/{member_id}/LinkedProfiles/?getAllMemberships={all}",
1113        )
1114        assert isinstance(resp, dict)
1115        return resp

Returns a summary information about all profiles linked to the requested member.

The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.

It will only return linked accounts whose linkages you are allowed to view.

Parameters
  • member_id (int): The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
  • member_type (aiobungie.aiobungie.MembershipType | int): The type for the membership whose linked Destiny account you want to return.
Other Parameters
  • all (bool): If provided and set to True, All memberships regardless of whether they're obscured by overrides will be returned,

    If provided and set to False, Only available memberships will be returned. The default for this is False.

Returns
async def fetch_clan_banners(self) -> collections.abc.Mapping[str, typing.Any]:
1117    async def fetch_clan_banners(self) -> typedefs.JSONObject:
1118        resp = await self._request(
1119            RequestMethod.GET, "Destiny2/Clan/ClanBannerDictionary/"
1120        )
1121        assert isinstance(resp, dict)
1122        return resp

Fetch the values of the clan banners.

Returns
async def fetch_public_milestones(self) -> collections.abc.Mapping[str, typing.Any]:
1124    async def fetch_public_milestones(self) -> typedefs.JSONObject:
1125        resp = await self._request(RequestMethod.GET, "Destiny2/Milestones/")
1126        assert isinstance(resp, dict)
1127        return resp

Fetch the available milestones.

Returns
async def fetch_public_milestone_content(self, milestone_hash: int, /) -> collections.abc.Mapping[str, typing.Any]:
1129    async def fetch_public_milestone_content(
1130        self, milestone_hash: int, /
1131    ) -> typedefs.JSONObject:
1132        resp = await self._request(
1133            RequestMethod.GET, f"Destiny2/Milestones/{milestone_hash}/Content/"
1134        )
1135        assert isinstance(resp, dict)
1136        return resp

Fetch the milestone content given its hash.

Parameters
  • milestone_hash (int): The milestone hash.
Returns
async def fetch_current_user_memberships(self, access_token: str, /) -> collections.abc.Mapping[str, typing.Any]:
1138    async def fetch_current_user_memberships(
1139        self, access_token: str, /
1140    ) -> typedefs.JSONObject:
1141        resp = await self._request(
1142            RequestMethod.GET,
1143            "User/GetMembershipsForCurrentUser/",
1144            auth=access_token,
1145        )
1146        assert isinstance(resp, dict)
1147        return resp

Fetch a bungie user's accounts with the signed in user. This GET method requires a Bearer access token for the authorization.

This requires OAuth2 scope enabled and the valid Bearer access_token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def equip_item( self, access_token: str, /, item_id: int, character_id: int, membership_type: MembershipType | int) -> None:
1149    async def equip_item(
1150        self,
1151        access_token: str,
1152        /,
1153        item_id: int,
1154        character_id: int,
1155        membership_type: enums.MembershipType | int,
1156    ) -> None:
1157        payload = {
1158            "itemId": item_id,
1159            "characterId": character_id,
1160            "membershipType": int(membership_type),
1161        }
1162
1163        await self._request(
1164            RequestMethod.POST,
1165            "Destiny2/Actions/Items/EquipItem/",
1166            json=payload,
1167            auth=access_token,
1168        )

Equip an item to a character.

This requires the OAuth2: MoveEquipDestinyItems scope. Also You must have a valid Destiny account, and either be in a social space, in orbit or offline.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item id.
  • character_id (int): The character's id to equip the item to.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The membership type associated with this player.
async def equip_items( self, access_token: str, /, item_ids: collections.abc.Sequence[int], character_id: int, membership_type: MembershipType | int) -> None:
1170    async def equip_items(
1171        self,
1172        access_token: str,
1173        /,
1174        item_ids: collections.Sequence[int],
1175        character_id: int,
1176        membership_type: enums.MembershipType | int,
1177    ) -> None:
1178        payload = {
1179            "itemIds": item_ids,
1180            "characterId": character_id,
1181            "membershipType": int(membership_type),
1182        }
1183        await self._request(
1184            RequestMethod.POST,
1185            "Destiny2/Actions/Items/EquipItems/",
1186            json=payload,
1187            auth=access_token,
1188        )

Equip multiple items to a character.

This requires the OAuth2: MoveEquipDestinyItems scope. Also You must have a valid Destiny account, and either be in a social space, in orbit or offline.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_ids (Sequence[int]): A sequence of item ids.
  • character_id (int): The character's id to equip the item to.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The membership type associated with this player.
async def ban_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: MembershipType | int, *, length: int = 0, comment: str | None = None) -> None:
1190    async def ban_clan_member(
1191        self,
1192        access_token: str,
1193        /,
1194        group_id: int,
1195        membership_id: int,
1196        membership_type: enums.MembershipType | int,
1197        *,
1198        length: int = 0,
1199        comment: str | None = None,
1200    ) -> None:
1201        payload = {"comment": str(comment), "length": length}
1202        await self._request(
1203            RequestMethod.POST,
1204            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Ban/",
1205            json=payload,
1206            auth=access_token,
1207        )

Bans a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to ban.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The member's membership type.
Other Parameters
  • length (int): An optional ban length.
  • comment (aiobungie.UndefinedOr[str]): An optional comment to this ban. Default is UNDEFINED
async def unban_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: MembershipType | int) -> None:
1209    async def unban_clan_member(
1210        self,
1211        access_token: str,
1212        /,
1213        group_id: int,
1214        membership_id: int,
1215        membership_type: enums.MembershipType | int,
1216    ) -> None:
1217        await self._request(
1218            RequestMethod.POST,
1219            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Unban/",
1220            auth=access_token,
1221        )

Unban a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to unban.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The member's membership type.
async def kick_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: MembershipType | int) -> collections.abc.Mapping[str, typing.Any]:
1223    async def kick_clan_member(
1224        self,
1225        access_token: str,
1226        /,
1227        group_id: int,
1228        membership_id: int,
1229        membership_type: enums.MembershipType | int,
1230    ) -> typedefs.JSONObject:
1231        resp = await self._request(
1232            RequestMethod.POST,
1233            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Kick/",
1234            auth=access_token,
1235        )
1236        assert isinstance(resp, dict)
1237        return resp

Kick a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to kick.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The member's membership type.
Returns
async def edit_clan( self, access_token: str, /, group_id: int, *, name: str | None = None, about: str | None = None, motto: str | None = None, theme: str | None = None, tags: collections.abc.Sequence[str] | None = None, is_public: bool | None = None, locale: str | None = None, avatar_image_index: int | None = None, membership_option: MembershipOption | int | None = None, allow_chat: bool | None = None, chat_security: Optional[Literal[0, 1]] = None, call_sign: str | None = None, homepage: Optional[Literal[0, 1, 2]] = None, enable_invite_messaging_for_admins: bool | None = None, default_publicity: Optional[Literal[0, 1, 2]] = None, is_public_topic_admin: bool | None = None) -> None:
1239    async def edit_clan(
1240        self,
1241        access_token: str,
1242        /,
1243        group_id: int,
1244        *,
1245        name: str | None = None,
1246        about: str | None = None,
1247        motto: str | None = None,
1248        theme: str | None = None,
1249        tags: collections.Sequence[str] | None = None,
1250        is_public: bool | None = None,
1251        locale: str | None = None,
1252        avatar_image_index: int | None = None,
1253        membership_option: enums.MembershipOption | int | None = None,
1254        allow_chat: bool | None = None,
1255        chat_security: typing.Literal[0, 1] | None = None,
1256        call_sign: str | None = None,
1257        homepage: typing.Literal[0, 1, 2] | None = None,
1258        enable_invite_messaging_for_admins: bool | None = None,
1259        default_publicity: typing.Literal[0, 1, 2] | None = None,
1260        is_public_topic_admin: bool | None = None,
1261    ) -> None:
1262        payload = {
1263            "name": name,
1264            "about": about,
1265            "motto": motto,
1266            "theme": theme,
1267            "tags": tags,
1268            "isPublic": is_public,
1269            "avatarImageIndex": avatar_image_index,
1270            "isPublicTopicAdminOnly": is_public_topic_admin,
1271            "allowChat": allow_chat,
1272            "chatSecurity": chat_security,
1273            "callsign": call_sign,
1274            "homepage": homepage,
1275            "enableInvitationMessagingForAdmins": enable_invite_messaging_for_admins,
1276            "defaultPublicity": default_publicity,
1277            "locale": locale,
1278        }
1279        if membership_option is not None:
1280            payload["membershipOption"] = int(membership_option)
1281
1282        await self._request(
1283            RequestMethod.POST,
1284            f"GroupV2/{group_id}/Edit",
1285            json=payload,
1286            auth=access_token,
1287        )

Edit a clan.

Notes
  • This request requires OAuth2: oauth2: AdminGroups scope.
  • All arguments will default to None if not provided. This does not include access_token and group_id
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id to edit.
Other Parameters
  • name (str | None): The name to edit the clan with.
  • about (str | None): The about section to edit the clan with.
  • motto (str | None): The motto section to edit the clan with.
  • theme (str | None): The theme name to edit the clan with.
  • tags (collections.Sequence[str] | None): A sequence of strings to replace the clan tags with.
  • is_public (bool | None): If provided and set to True, The clan will set to private. If provided and set to False, The clan will set to public whether it was or not.
  • locale (str | None): The locale section to edit the clan with.
  • avatar_image_index (int | None): The clan avatar image index to edit the clan with.
  • membership_option : aiobungie.typedefs.NoneOr[aiobungie.aiobungie.MembershipOption | int] # noqa (E501 # Line too long): The clan membership option to edit it with.
  • allow_chat (bool | None): If provided and set to True, The clan members will be allowed to chat. If provided and set to False, The clan members will not be allowed to chat.
  • chat_security (aiobungie.typedefs.NoneOr[typing.Literal[0, 1]]): If provided and set to 0, The clan chat security will be edited to Group only. If provided and set to 1, The clan chat security will be edited to Admin only.
  • call_sign (str | None): The clan call sign to edit it with.
  • homepage (aiobungie.typedefs.NoneOr[typing.Literal[0, 1, 2]]): If provided and set to 0, The clan chat homepage will be edited to Wall. If provided and set to 1, The clan chat homepage will be edited to Forum. If provided and set to 0, The clan chat homepage will be edited to AllianceForum.
  • enable_invite_messaging_for_admins (bool | None): ???
  • default_publicity (aiobungie.typedefs.NoneOr[typing.Literal[0, 1, 2]]): If provided and set to 0, The clan chat publicity will be edited to Public. If provided and set to 1, The clan chat publicity will be edited to Alliance. If provided and set to 2, The clan chat publicity will be edited to Private.
  • is_public_topic_admin (bool | None): ???
async def edit_clan_options( self, access_token: str, /, group_id: int, *, invite_permissions_override: bool | None = None, update_culture_permissionOverride: bool | None = None, host_guided_game_permission_override: Optional[Literal[0, 1, 2]] = None, update_banner_permission_override: bool | None = None, join_level: ClanMemberType | int | None = None) -> None:
1289    async def edit_clan_options(
1290        self,
1291        access_token: str,
1292        /,
1293        group_id: int,
1294        *,
1295        invite_permissions_override: bool | None = None,
1296        update_culture_permissionOverride: bool | None = None,
1297        host_guided_game_permission_override: typing.Literal[0, 1, 2] | None = None,
1298        update_banner_permission_override: bool | None = None,
1299        join_level: enums.ClanMemberType | int | None = None,
1300    ) -> None:
1301        payload = {
1302            "InvitePermissionOverride": invite_permissions_override,
1303            "UpdateCulturePermissionOverride": update_culture_permissionOverride,
1304            "HostGuidedGamePermissionOverride": host_guided_game_permission_override,
1305            "UpdateBannerPermissionOverride": update_banner_permission_override,
1306            "JoinLevel": int(join_level) if join_level else None,
1307        }
1308
1309        await self._request(
1310            RequestMethod.POST,
1311            f"GroupV2/{group_id}/EditFounderOptions",
1312            json=payload,
1313            auth=access_token,
1314        )

Edit the clan options.

Notes
  • This request requires OAuth2: oauth2: AdminGroups scope.
  • All arguments will default to None if not provided. This does not include access_token and group_id
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
Other Parameters
  • invite_permissions_override (bool | None): Minimum Member Level allowed to invite new members to group Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • update_culture_permissionOverride (bool | None): Minimum Member Level allowed to update group culture Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • host_guided_game_permission_override (aiobungie.typedefs.NoneOr[typing.Literal[0, 1, 2]]): Minimum Member Level allowed to host guided games Always Allowed: Founder, Acting Founder, Admin Allowed Overrides: 0 -> None, 1 -> Beginner 2 -> Member. Default is Member for clans, None for groups, although this means nothing for groups.
  • update_banner_permission_override (bool | None): Minimum Member Level allowed to update banner Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • join_level (aiobungie.ClanMemberType): Level to join a member at when accepting an invite, application, or joining an open clan. Default is aiobungie.ClanMemberType.BEGINNER
async def fetch_friends(self, access_token: str, /) -> collections.abc.Mapping[str, typing.Any]:
1316    async def fetch_friends(self, access_token: str, /) -> typedefs.JSONObject:
1317        resp = await self._request(
1318            RequestMethod.GET,
1319            "Social/Friends/",
1320            auth=access_token,
1321        )
1322        assert isinstance(resp, dict)
1323        return resp

Fetch bungie friend list.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_friend_requests(self, access_token: str, /) -> collections.abc.Mapping[str, typing.Any]:
1325    async def fetch_friend_requests(self, access_token: str, /) -> typedefs.JSONObject:
1326        resp = await self._request(
1327            RequestMethod.GET,
1328            "Social/Friends/Requests",
1329            auth=access_token,
1330        )
1331        assert isinstance(resp, dict)
1332        return resp

Fetch pending bungie friend requests queue.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1334    async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1335        await self._request(
1336            RequestMethod.POST,
1337            f"Social/Friends/Requests/Accept/{member_id}",
1338            auth=access_token,
1339        )

Accepts a friend relationship with the target user. The user must be on your incoming friend request list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to accept.
async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1341    async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1342        await self._request(
1343            RequestMethod.POST,
1344            f"Social/Friends/Add/{member_id}",
1345            auth=access_token,
1346        )

Requests a friend relationship with the target user.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to send the request to.
async def decline_friend_request(self, access_token: str, /, member_id: int) -> None:
1348    async def decline_friend_request(
1349        self, access_token: str, /, member_id: int
1350    ) -> None:
1351        await self._request(
1352            RequestMethod.POST,
1353            f"Social/Friends/Requests/Decline/{member_id}",
1354            auth=access_token,
1355        )

Decline a friend request with the target user. The user must be in your incoming friend request list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to decline.
async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1357    async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1358        await self._request(
1359            RequestMethod.POST,
1360            f"Social/Friends/Remove/{member_id}",
1361            auth=access_token,
1362        )

Removes a friend from your friend list. The user must be in your friend list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to remove.
async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1364    async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1365        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1366        await self._request(
1367            RequestMethod.POST,
1368            f"Social/Friends/Requests/Remove/{member_id}",
1369            auth=access_token,
1370        )

Removes a friend from your friend list requests. The user must be in your outgoing request list.

.. note : This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to remove from the requested friend list.
async def approve_all_pending_group_users( self, access_token: str, /, group_id: int, message: str | None = None) -> None:
1372    async def approve_all_pending_group_users(
1373        self,
1374        access_token: str,
1375        /,
1376        group_id: int,
1377        message: str | None = None,
1378    ) -> None:
1379        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1380        await self._request(
1381            RequestMethod.POST,
1382            f"GroupV2/{group_id}/Members/ApproveAll",
1383            auth=access_token,
1384            json={"message": str(message)},
1385        )

Approve all pending users for the given group id.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other Parameters
  • message (aiobungie.UndefinedOr[str]): An optional message to send with the request. Default is UNDEFINED.
async def deny_all_pending_group_users( self, access_token: str, /, group_id: int, *, message: str | None = None) -> None:
1387    async def deny_all_pending_group_users(
1388        self,
1389        access_token: str,
1390        /,
1391        group_id: int,
1392        *,
1393        message: str | None = None,
1394    ) -> None:
1395        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1396        await self._request(
1397            RequestMethod.POST,
1398            f"GroupV2/{group_id}/Members/DenyAll",
1399            auth=access_token,
1400            json={"message": str(message)},
1401        )

Deny all pending users for the given group id.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other Parameters
  • message (aiobungie.UndefinedOr[str]): An optional message to send with the request. Default is UNDEFINED.
async def add_optional_conversation( self, access_token: str, /, group_id: int, *, name: str | None = None, security: Literal[0, 1] = 0) -> None:
1403    async def add_optional_conversation(
1404        self,
1405        access_token: str,
1406        /,
1407        group_id: int,
1408        *,
1409        name: str | None = None,
1410        security: typing.Literal[0, 1] = 0,
1411    ) -> None:
1412        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1413        payload = {"chatName": str(name), "chatSecurity": security}
1414        await self._request(
1415            RequestMethod.POST,
1416            f"GroupV2/{group_id}/OptionalConversations/Add",
1417            json=payload,
1418            auth=access_token,
1419        )

Add a new chat channel to a group.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other parameters

name: aiobungie.UndefinedOr[str] The chat name. Default to UNDEFINED security: typing.Literal[0, 1] The security level of the chat.

If provided and set to 0, It will be to `Group` only.
If provided and set to 1, It will be `Admins` only.
Default is `0`
async def edit_optional_conversation( self, access_token: str, /, group_id: int, conversation_id: int, *, name: str | None = None, security: Literal[0, 1] = 0, enable_chat: bool = False) -> None:
1421    async def edit_optional_conversation(
1422        self,
1423        access_token: str,
1424        /,
1425        group_id: int,
1426        conversation_id: int,
1427        *,
1428        name: str | None = None,
1429        security: typing.Literal[0, 1] = 0,
1430        enable_chat: bool = False,
1431    ) -> None:
1432        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1433        payload = {
1434            "chatEnabled": enable_chat,
1435            "chatName": str(name),
1436            "chatSecurity": security,
1437        }
1438        await self._request(
1439            RequestMethod.POST,
1440            f"GroupV2/{group_id}/OptionalConversations/Edit/{conversation_id}",
1441            json=payload,
1442            auth=access_token,
1443        )

Edit the settings of this chat channel.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
  • conversation_id (int): The conversation/chat id.
Other parameters

name: aiobungie.UndefinedOr[str] The new chat name. Default to UNDEFINED security: typing.Literal[0, 1] The new security level of the chat.

If provided and set to 0, It will be to `Group` only.
If provided and set to 1, It will be `Admins` only.
Default is `0`

enable_chat : bool Whether to enable chatting or not. If set to True then chatting will be enabled. Otherwise it will be disabled.

async def transfer_item( self, access_token: str, /, item_id: int, item_hash: int, character_id: int, member_type: MembershipType | int, *, stack_size: int = 1, vault: bool = False) -> None:
1445    async def transfer_item(
1446        self,
1447        access_token: str,
1448        /,
1449        item_id: int,
1450        item_hash: int,
1451        character_id: int,
1452        member_type: enums.MembershipType | int,
1453        *,
1454        stack_size: int = 1,
1455        vault: bool = False,
1456    ) -> None:
1457        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1458        payload = {
1459            "characterId": character_id,
1460            "membershipType": int(member_type),
1461            "itemId": item_id,
1462            "itemReferenceHash": item_hash,
1463            "stackSize": stack_size,
1464            "transferToVault": vault,
1465        }
1466        await self._request(
1467            RequestMethod.POST,
1468            "Destiny2/Actions/Items/TransferItem",
1469            json=payload,
1470            auth=access_token,
1471        )

Transfer an item from / to your vault.

Notes
  • This method requires OAuth2: MoveEquipDestinyItems scope.
  • This method requires both item id and hash.
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item instance id you to transfer.
  • item_hash (int): The item hash.
  • character_id (int): The character id to transfer the item from/to.
  • member_type (aiobungie.aiobungie.MembershipType | int): The user membership type.
Other Parameters
  • stack_size (int): The item stack size.
  • vault (bool): Whether to transfer this item to your vault or not. Defaults to False.
async def pull_item( self, access_token: str, /, item_id: int, item_hash: int, character_id: int, member_type: MembershipType | int, *, stack_size: int = 1, vault: bool = False) -> None:
1473    async def pull_item(
1474        self,
1475        access_token: str,
1476        /,
1477        item_id: int,
1478        item_hash: int,
1479        character_id: int,
1480        member_type: enums.MembershipType | int,
1481        *,
1482        stack_size: int = 1,
1483        vault: bool = False,
1484    ) -> None:
1485        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1486        payload = {
1487            "characterId": character_id,
1488            "membershipType": int(member_type),
1489            "itemId": item_id,
1490            "itemReferenceHash": item_hash,
1491            "stackSize": stack_size,
1492            "transferToVault": vault,
1493        }
1494        await self._request(
1495            RequestMethod.POST,
1496            "Destiny2/Actions/Items/PullFromPostmaster",
1497            json=payload,
1498            auth=access_token,
1499        )

pull an item from the postmaster.

Notes
  • This method requires OAuth2: MoveEquipDestinyItems scope.
  • This method requires both item id and hash.
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item instance id to pull.
  • item_hash (int): The item hash.
  • character_id (int): The character id to pull the item to.
  • member_type (aiobungie.aiobungie.MembershipType | int): The user membership type.
Other Parameters
  • stack_size (int): The item stack size.
  • vault (bool): Whether to pill this item to your vault or not. Defaults to False.
async def fetch_fireteams( self, activity_type: FireteamActivity | int, *, platform: FireteamPlatform | int = <FireteamPlatform.ANY: 0>, language: FireteamLanguage | str = <FireteamLanguage.ALL: >, date_range: FireteamDate | int = <FireteamDate.ALL: 0>, page: int = 0, slots_filter: int = 0) -> collections.abc.Mapping[str, typing.Any]:
1501    async def fetch_fireteams(
1502        self,
1503        activity_type: fireteams.FireteamActivity | int,
1504        *,
1505        platform: fireteams.FireteamPlatform | int = fireteams.FireteamPlatform.ANY,
1506        language: fireteams.FireteamLanguage | str = fireteams.FireteamLanguage.ALL,
1507        date_range: fireteams.FireteamDate | int = fireteams.FireteamDate.ALL,
1508        page: int = 0,
1509        slots_filter: int = 0,
1510    ) -> typedefs.JSONObject:
1511        resp = await self._request(
1512            RequestMethod.GET,
1513            f"Fireteam/Search/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{page}/?langFilter={str(language)}",  # noqa: E501 Line too long
1514        )
1515        assert isinstance(resp, dict)
1516        return resp

Fetch public Bungie fireteams with open slots.

Parameters
  • activity_type (aiobungie.aiobungie.crates.FireteamActivity | int): The fireteam activity type.
Other Parameters
  • platform (aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (aiobungie.aiobungie.FireteamDate | int): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_available_clan_fireteams( self, access_token: str, group_id: int, activity_type: FireteamActivity | int, *, platform: FireteamPlatform | int, language: FireteamLanguage | str, date_range: FireteamDate | int = <FireteamDate.ALL: 0>, page: int = 0, public_only: bool = False, slots_filter: int = 0) -> collections.abc.Mapping[str, typing.Any]:
1518    async def fetch_available_clan_fireteams(
1519        self,
1520        access_token: str,
1521        group_id: int,
1522        activity_type: fireteams.FireteamActivity | int,
1523        *,
1524        platform: fireteams.FireteamPlatform | int,
1525        language: fireteams.FireteamLanguage | str,
1526        date_range: fireteams.FireteamDate | int = fireteams.FireteamDate.ALL,
1527        page: int = 0,
1528        public_only: bool = False,
1529        slots_filter: int = 0,
1530    ) -> typedefs.JSONObject:
1531        resp = await self._request(
1532            RequestMethod.GET,
1533            f"Fireteam/Clan/{group_id}/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{public_only}/{page}",  # noqa: E501
1534            json={"langFilter": str(language)},
1535            auth=access_token,
1536        )
1537        assert isinstance(resp, dict)
1538        return resp

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id of the fireteam.
  • activity_type (aiobungie.aiobungie.crates.FireteamActivity | int): The fireteam activity type.
Other Parameters
  • platform (aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (aiobungie.aiobungie.FireteamDate | int): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • public_only (bool): If set to True, Then only public fireteams will be returned.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_clan_fireteam( self, access_token: str, fireteam_id: int, group_id: int) -> collections.abc.Mapping[str, typing.Any]:
1540    async def fetch_clan_fireteam(
1541        self, access_token: str, fireteam_id: int, group_id: int
1542    ) -> typedefs.JSONObject:
1543        resp = await self._request(
1544            RequestMethod.GET,
1545            f"Fireteam/Clan/{group_id}/Summary/{fireteam_id}",
1546            auth=access_token,
1547        )
1548        assert isinstance(resp, dict)
1549        return resp

Fetch a specific clan fireteam.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch the fireteam from.
  • fireteam_id (int): The fireteam id to fetch.
Returns
async def fetch_my_clan_fireteams( self, access_token: str, group_id: int, *, include_closed: bool = True, platform: FireteamPlatform | int, language: FireteamLanguage | str, filtered: bool = True, page: int = 0) -> collections.abc.Mapping[str, typing.Any]:
1551    async def fetch_my_clan_fireteams(
1552        self,
1553        access_token: str,
1554        group_id: int,
1555        *,
1556        include_closed: bool = True,
1557        platform: fireteams.FireteamPlatform | int,
1558        language: fireteams.FireteamLanguage | str,
1559        filtered: bool = True,
1560        page: int = 0,
1561    ) -> typedefs.JSONObject:
1562        payload = {"groupFilter": filtered, "langFilter": str(language)}
1563
1564        resp = await self._request(
1565            RequestMethod.GET,
1566            f"Fireteam/Clan/{group_id}/My/{int(platform)}/{include_closed}/{page}",
1567            json=payload,
1568            auth=access_token,
1569        )
1570        assert isinstance(resp, dict)
1571        return resp

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch.
Other Parameters
  • include_closed (bool): If provided and set to True, It will also return closed fireteams. If provided and set to False, It will only return public fireteams. Default is True.
  • platform (aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • filtered (bool): If set to True, it will filter by clan. Otherwise not. Default is True.
  • page (int): The page number. By default its 0 which returns all available activities.
Returns
async def fetch_private_clan_fireteams(self, access_token: str, group_id: int, /) -> int:
1573    async def fetch_private_clan_fireteams(
1574        self, access_token: str, group_id: int, /
1575    ) -> int:
1576        resp = await self._request(
1577            RequestMethod.GET,
1578            f"Fireteam/Clan/{group_id}/ActiveCount",
1579            auth=access_token,
1580        )
1581        assert isinstance(resp, int)
1582        return resp

Fetch the active count of the clan fireteams that are only private.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id.
Returns
  • int: The active fireteams count. Max value returned is 25.
async def fetch_post_activity(self, instance_id: int, /) -> collections.abc.Mapping[str, typing.Any]:
1584    async def fetch_post_activity(self, instance_id: int, /) -> typedefs.JSONObject:
1585        resp = await self._request(
1586            RequestMethod.GET, f"Destiny2/Stats/PostGameCarnageReport/{instance_id}"
1587        )
1588        assert isinstance(resp, dict)
1589        return resp

Fetch a post activity details.

Parameters
  • instance_id (int): The activity instance id.
Returns
async def search_entities( self, name: str, entity_type: str, *, page: int = 0) -> collections.abc.Mapping[str, typing.Any]:
1591    async def search_entities(
1592        self, name: str, entity_type: str, *, page: int = 0
1593    ) -> typedefs.JSONObject:
1594        resp = await self._request(
1595            RequestMethod.GET,
1596            f"Destiny2/Armory/Search/{entity_type}/{name}/",
1597            json={"page": page},
1598        )
1599        assert isinstance(resp, dict)
1600        return resp

Search for Destiny2 entities given a name and its type.

Parameters
  • name (str): The name of the entity, i.e., Thunderlord, One thousand voices.
  • entity_type (str): The type of the entity, AKA Definition, For an example DestinyInventoryItemDefinition
Other Parameters
  • page (int): An optional page to return. Default to 0.
Returns
async def fetch_unique_weapon_history( self, membership_id: int, character_id: int, membership_type: MembershipType | int) -> collections.abc.Mapping[str, typing.Any]:
1602    async def fetch_unique_weapon_history(
1603        self,
1604        membership_id: int,
1605        character_id: int,
1606        membership_type: enums.MembershipType | int,
1607    ) -> typedefs.JSONObject:
1608        resp = await self._request(
1609            RequestMethod.GET,
1610            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/UniqueWeapons/",
1611        )
1612        assert isinstance(resp, dict)
1613        return resp

Fetch details about unique weapon usage for a character. Includes all exotics.

Parameters
  • membership_id (int): The Destiny user membership id.
  • character_id (int): The character id to retrieve.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The Destiny user's membership type.
Returns
async def fetch_item( self, member_id: int, item_id: int, membership_type: MembershipType | int, components: list[ComponentType]) -> collections.abc.Mapping[str, typing.Any]:
1615    async def fetch_item(
1616        self,
1617        member_id: int,
1618        item_id: int,
1619        membership_type: enums.MembershipType | int,
1620        components: list[enums.ComponentType],
1621    ) -> typedefs.JSONObject:
1622        collector = _collect_components(components)
1623
1624        resp = await self._request(
1625            RequestMethod.GET,
1626            f"Destiny2/{int(membership_type)}/Profile/{member_id}/Item/{item_id}/?components={collector}",
1627        )
1628        assert isinstance(resp, dict)
1629        return resp

Fetch an instanced Destiny 2 item's details.

Parameters
  • member_id (int): The membership id of the Destiny 2 player.
  • item_id (int): The instance id of the item.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The membership type of the Destiny 2 player.
  • components (list[aiobungie.ComponentType]): A list of components to retrieve.
Returns
async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> collections.abc.Mapping[str, typing.Any]:
1631    async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> typedefs.JSONObject:
1632        resp = await self._request(
1633            RequestMethod.GET, f"Destiny2/Clan/{clan_id}/WeeklyRewardState/"
1634        )
1635        assert isinstance(resp, dict)
1636        return resp

Fetch the weekly reward state for a clan.

Parameters
  • clan_id (int): The clan id.
Returns
async def fetch_available_locales(self) -> collections.abc.Mapping[str, typing.Any]:
1638    async def fetch_available_locales(self) -> typedefs.JSONObject:
1639        resp = await self._request(
1640            RequestMethod.GET, "Destiny2/Manifest/DestinyLocaleDefinition/"
1641        )
1642        assert isinstance(resp, dict)
1643        return resp

Fetch available locales at Bungie.

Returns
async def fetch_common_settings(self) -> collections.abc.Mapping[str, typing.Any]:
1645    async def fetch_common_settings(self) -> typedefs.JSONObject:
1646        resp = await self._request(RequestMethod.GET, "Settings")
1647        assert isinstance(resp, dict)
1648        return resp

Fetch the common settings used by Bungie's environment.

Returns
async def fetch_user_systems_overrides(self) -> collections.abc.Mapping[str, typing.Any]:
1650    async def fetch_user_systems_overrides(self) -> typedefs.JSONObject:
1651        resp = await self._request(RequestMethod.GET, "UserSystemOverrides")
1652        assert isinstance(resp, dict)
1653        return resp

Fetch a user's specific system overrides.

Returns
async def fetch_global_alerts( self, *, include_streaming: bool = False) -> collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]:
1655    async def fetch_global_alerts(
1656        self, *, include_streaming: bool = False
1657    ) -> typedefs.JSONArray:
1658        resp = await self._request(
1659            RequestMethod.GET, f"GlobalAlerts/?includestreaming={include_streaming}"
1660        )
1661        assert isinstance(resp, list)
1662        return resp

Fetch any active global alerts.

Parameters
  • include_streaming (bool): If True, the returned results will include streaming alerts. Default is False.
Returns
async def awainitialize_request( self, access_token: str, type: Literal[0, 1], membership_type: MembershipType | int, /, *, affected_item_id: int | None = None, character_id: int | None = None) -> collections.abc.Mapping[str, typing.Any]:
1664    async def awainitialize_request(
1665        self,
1666        access_token: str,
1667        type: typing.Literal[0, 1],
1668        membership_type: enums.MembershipType | int,
1669        /,
1670        *,
1671        affected_item_id: int | None = None,
1672        character_id: int | None = None,
1673    ) -> typedefs.JSONObject:
1674        body = {"type": type, "membershipType": int(membership_type)}
1675
1676        if affected_item_id is not None:
1677            body["affectedItemId"] = affected_item_id
1678
1679        if character_id is not None:
1680            body["characterId"] = character_id
1681
1682        resp = await self._request(
1683            RequestMethod.POST, "Destiny2/Awa/Initialize", json=body, auth=access_token
1684        )
1685        assert isinstance(resp, dict)
1686        return resp

Initialize a request to perform an advanced write action.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • type (typing.Literal[0, 1]): Type of the advanced write action. Its either 0 or 1. If set to 0 that means it None. Otherwise if 1 that means its insert plugs.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The Destiny membership type of the account to modify.
Other Parameters
  • affected_item_id (int | None): Item instance ID the action shall be applied to. This is optional for all but a new AwaType values.
  • character_id (int | None): The Destiny character ID to perform this action on.
Returns
async def awaget_action_token( self, access_token: str, correlation_id: str, /) -> collections.abc.Mapping[str, typing.Any]:
1688    async def awaget_action_token(
1689        self, access_token: str, correlation_id: str, /
1690    ) -> typedefs.JSONObject:
1691        resp = await self._request(
1692            RequestMethod.POST,
1693            f"Destiny2/Awa/GetActionToken/{correlation_id}",
1694            auth=access_token,
1695        )
1696        assert isinstance(resp, dict)
1697        return resp

Returns the action token if user approves the request.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • correlation_id (str): The identifier for the advanced write action request.
Returns
async def awa_provide_authorization_result( self, access_token: str, selection: int, correlation_id: str, nonce: collections.abc.MutableSequence[str | bytes]) -> int:
1699    async def awa_provide_authorization_result(
1700        self,
1701        access_token: str,
1702        selection: int,
1703        correlation_id: str,
1704        nonce: collections.MutableSequence[str | bytes],
1705    ) -> int:
1706        body = {"selection": selection, "correlationId": correlation_id, "nonce": nonce}
1707
1708        resp = await self._request(
1709            RequestMethod.POST,
1710            "Destiny2/Awa/AwaProvideAuthorizationResult",
1711            json=body,
1712            auth=access_token,
1713        )
1714        assert isinstance(resp, int)
1715        return resp

Provide the result of the user interaction. Called by the Bungie Destiny App to approve or reject a request.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • selection (int): Indication of the selection the user has made (Approving or rejecting the action)
  • correlation_id (str): Correlation ID of the request.
  • nonce (collections.MutableSequence[str | bytes]): Secret nonce received via the PUSH notification.
Returns
  • int: ...
async def fetch_vendors( self, access_token: str, character_id: int, membership_id: int, membership_type: MembershipType | int, /, components: list[ComponentType], filter: int | None = None) -> collections.abc.Mapping[str, typing.Any]:
1717    async def fetch_vendors(
1718        self,
1719        access_token: str,
1720        character_id: int,
1721        membership_id: int,
1722        membership_type: enums.MembershipType | int,
1723        /,
1724        components: list[enums.ComponentType],
1725        filter: int | None = None,
1726    ) -> typedefs.JSONObject:
1727        components_ = _collect_components(components)
1728        route = (
1729            f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1730            f"/Character/{character_id}/Vendors/?components={components_}"
1731        )
1732
1733        if filter is not None:
1734            route = route + f"&filter={filter}"
1735
1736        resp = await self._request(
1737            RequestMethod.GET,
1738            route,
1739            auth=access_token,
1740        )
1741        assert isinstance(resp, dict)
1742        return resp

Get currently available vendors from the list of vendors that can possibly have rotating inventory.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • character_id (int): The character ID to return the vendor info for.
  • membership_id (int): The Destiny membership id to return the vendor info for.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The Destiny membership type to return the vendor info for.
  • components (list[aiobungie.ComponentType]): A list of vendor components to collect and return.
Other Parameters
  • filter (int): Filters the type of items returned from the vendor. This can be left to None.
Returns
async def fetch_vendor( self, access_token: str, character_id: int, membership_id: int, membership_type: MembershipType | int, vendor_hash: int, /, components: list[ComponentType]) -> collections.abc.Mapping[str, typing.Any]:
1744    async def fetch_vendor(
1745        self,
1746        access_token: str,
1747        character_id: int,
1748        membership_id: int,
1749        membership_type: enums.MembershipType | int,
1750        vendor_hash: int,
1751        /,
1752        components: list[enums.ComponentType],
1753    ) -> typedefs.JSONObject:
1754        components_ = _collect_components(components)
1755        resp = await self._request(
1756            RequestMethod.GET,
1757            (
1758                f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1759                f"/Character/{character_id}/Vendors/{vendor_hash}/?components={components_}"
1760            ),
1761            auth=access_token,
1762        )
1763        assert isinstance(resp, dict)
1764        return resp

Fetch details for a specific vendor.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • character_id (int): The character ID to return the vendor info for.
  • membership_id (int): The Destiny membership id to return the vendor info for.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The Destiny membership type to return the vendor info for.
  • vendor_hash (int): The vendor hash to return the details for.
  • components (list[aiobungie.ComponentType]): A list of vendor components to collect and return.
Returns
async def fetch_application_api_usage( self, access_token: str, application_id: int, /, *, start: datetime.datetime | None = None, end: datetime.datetime | None = None) -> collections.abc.Mapping[str, typing.Any]:
1766    async def fetch_application_api_usage(
1767        self,
1768        access_token: str,
1769        application_id: int,
1770        /,
1771        *,
1772        start: datetime.datetime | None = None,
1773        end: datetime.datetime | None = None,
1774    ) -> typedefs.JSONObject:
1775        end_date, start_date = time.parse_date_range(end, start)
1776        resp = await self._request(
1777            RequestMethod.GET,
1778            f"App/ApiUsage/{application_id}/?end={end_date}&start={start_date}",
1779            auth=access_token,
1780        )
1781        assert isinstance(resp, dict)
1782        return resp

Fetch a Bungie application's API usage.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • application_id (int): The application id to get.
Other Parameters
  • start (datetime.datetime | None): A datetime object can be used to collect the start of the application usage. This is limited and can go back to 30 days maximum.

    If this is left to None. It will return the last 24 hours.

  • end (datetime.datetime | None): A datetime object can be used to collect the end of the application usage.

    If this is left to None. It will return now.

Example
import datetime

# Fetch data from 2021 Dec 10th to 2021 Dec 20th
await fetch_application_api_usage(
    start=datetime.datetime(2021, 12, 10),
    end=datetime.datetime(2021, 12, 20)
)
Returns
async def fetch_bungie_applications( self) -> collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]:
1784    async def fetch_bungie_applications(self) -> typedefs.JSONArray:
1785        resp = await self._request(RequestMethod.GET, "App/FirstParty")
1786        assert isinstance(resp, list)
1787        return resp

Fetch details for applications created by Bungie.

Returns
async def fetch_content_type(self, type: str, /) -> collections.abc.Mapping[str, typing.Any]:
1789    async def fetch_content_type(self, type: str, /) -> typedefs.JSONObject:
1790        resp = await self._request(RequestMethod.GET, f"Content/GetContentType/{type}/")
1791        assert isinstance(resp, dict)
1792        return resp
async def fetch_content_by_id( self, id: int, locale: str, /, *, head: bool = False) -> collections.abc.Mapping[str, typing.Any]:
1794    async def fetch_content_by_id(
1795        self, id: int, locale: str, /, *, head: bool = False
1796    ) -> typedefs.JSONObject:
1797        resp = await self._request(
1798            RequestMethod.GET,
1799            f"Content/GetContentById/{id}/{locale}/",
1800            json={"head": head},
1801        )
1802        assert isinstance(resp, dict)
1803        return resp
async def fetch_content_by_tag_and_type( self, locale: str, tag: str, type: str, *, head: bool = False) -> collections.abc.Mapping[str, typing.Any]:
1805    async def fetch_content_by_tag_and_type(
1806        self, locale: str, tag: str, type: str, *, head: bool = False
1807    ) -> typedefs.JSONObject:
1808        resp = await self._request(
1809            RequestMethod.GET,
1810            f"Content/GetContentByTagAndType/{tag}/{type}/{locale}/",
1811            json={"head": head},
1812        )
1813        assert isinstance(resp, dict)
1814        return resp
async def search_content_with_text( self, locale: str, /, content_type: str, search_text: str, tag: str, *, page: int | None = None, source: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
1816    async def search_content_with_text(
1817        self,
1818        locale: str,
1819        /,
1820        content_type: str,
1821        search_text: str,
1822        tag: str,
1823        *,
1824        page: int | None = None,
1825        source: str | None = None,
1826    ) -> typedefs.JSONObject:
1827        body: typedefs.JSONObject = {
1828            "locale": locale,
1829            "currentpage": page or 1,
1830            "ctype": content_type,
1831            "searchtxt": search_text,
1832            "ctype": content_type,
1833            "searchtext": search_text,
1834            "tag": tag,
1835            "source": source,
1836        }
1837
1838        resp = await self._request(RequestMethod.GET, "Content/Search", params=body)
1839        assert isinstance(resp, dict)
1840        return resp
async def search_content_by_tag_and_type( self, locale: str, tag: str, type: str, *, page: int | None = None) -> collections.abc.Mapping[str, typing.Any]:
1842    async def search_content_by_tag_and_type(
1843        self,
1844        locale: str,
1845        tag: str,
1846        type: str,
1847        *,
1848        page: int | None = None,
1849    ) -> typedefs.JSONObject:
1850        body: typedefs.JSONObject = {"currentpage": page or 1}
1851
1852        resp = await self._request(
1853            RequestMethod.GET,
1854            f"Content/SearchContentByTagAndType/{tag}/{type}/{locale}/",
1855            params=body,
1856        )
1857        assert isinstance(resp, dict)
1858        return resp
async def search_help_articles( self, text: str, size: str, /) -> collections.abc.Mapping[str, typing.Any]:
1860    async def search_help_articles(
1861        self, text: str, size: str, /
1862    ) -> typedefs.JSONObject:
1863        resp = await self._request(
1864            RequestMethod.GET, f"Content/SearchHelpArticles/{text}/{size}/"
1865        )
1866        assert isinstance(resp, dict)
1867        return resp
async def fetch_topics_page( self, category_filter: int, group: int, date_filter: int, sort: str | bytes, *, page: int | None = None, locales: collections.abc.Iterable[str] | None = None, tag_filter: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
1869    async def fetch_topics_page(
1870        self,
1871        category_filter: int,
1872        group: int,
1873        date_filter: int,
1874        sort: str | bytes,
1875        *,
1876        page: int | None = None,
1877        locales: collections.Iterable[str] | None = None,
1878        tag_filter: str | None = None,
1879    ) -> typedefs.JSONObject:
1880        params = {
1881            "locales": ",".join(locales) if locales is not None else "en",
1882        }
1883        if tag_filter:
1884            params["tagstring"] = tag_filter
1885
1886        resp = await self._request(
1887            RequestMethod.GET,
1888            f"Forum/GetTopicsPaged/{page or 0}/0/{group}/{sort!s}/{date_filter}/{category_filter}/",
1889            params=params,
1890        )
1891        assert isinstance(resp, dict)
1892        return resp
async def fetch_core_topics_page( self, category_filter: int, date_filter: int, sort: str | bytes, *, page: int | None = None, locales: collections.abc.Iterable[str] | None = None) -> collections.abc.Mapping[str, typing.Any]:
1894    async def fetch_core_topics_page(
1895        self,
1896        category_filter: int,
1897        date_filter: int,
1898        sort: str | bytes,
1899        *,
1900        page: int | None = None,
1901        locales: collections.Iterable[str] | None = None,
1902    ) -> typedefs.JSONObject:
1903        resp = await self._request(
1904            RequestMethod.GET,
1905            f"Forum/GetCoreTopicsPaged/{page or 0}"
1906            f"/{sort!s}/{date_filter}/{category_filter}/?locales={','.join(locales) if locales else 'en'}",
1907        )
1908        assert isinstance(resp, dict)
1909        return resp
async def fetch_posts_threaded_page( self, parent_post: bool, page: int, page_size: int, parent_post_id: int, reply_size: int, root_thread_mode: bool, sort_mode: int, show_banned: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
1911    async def fetch_posts_threaded_page(
1912        self,
1913        parent_post: bool,
1914        page: int,
1915        page_size: int,
1916        parent_post_id: int,
1917        reply_size: int,
1918        root_thread_mode: bool,
1919        sort_mode: int,
1920        show_banned: str | None = None,
1921    ) -> typedefs.JSONObject:
1922        resp = await self._request(
1923            RequestMethod.GET,
1924            f"Forum/GetPostsThreadedPaged/{parent_post}/{page}/"
1925            f"{page_size}/{reply_size}/{parent_post_id}/{root_thread_mode}/{sort_mode}/",
1926            json={"showbanned": show_banned},
1927        )
1928        assert isinstance(resp, dict)
1929        return resp
async def fetch_posts_threaded_page_from_child( self, child_id: bool, page: int, page_size: int, reply_size: int, root_thread_mode: bool, sort_mode: int, show_banned: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
1931    async def fetch_posts_threaded_page_from_child(
1932        self,
1933        child_id: bool,
1934        page: int,
1935        page_size: int,
1936        reply_size: int,
1937        root_thread_mode: bool,
1938        sort_mode: int,
1939        show_banned: str | None = None,
1940    ) -> typedefs.JSONObject:
1941        resp = await self._request(
1942            RequestMethod.GET,
1943            f"Forum/GetPostsThreadedPagedFromChild/{child_id}/"
1944            f"{page}/{page_size}/{reply_size}/{root_thread_mode}/{sort_mode}/",
1945            json={"showbanned": show_banned},
1946        )
1947        assert isinstance(resp, dict)
1948        return resp
async def fetch_post_and_parent( self, child_id: int, /, *, show_banned: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
1950    async def fetch_post_and_parent(
1951        self, child_id: int, /, *, show_banned: str | None = None
1952    ) -> typedefs.JSONObject:
1953        resp = await self._request(
1954            RequestMethod.GET,
1955            f"Forum/GetPostAndParent/{child_id}/",
1956            json={"showbanned": show_banned},
1957        )
1958        assert isinstance(resp, dict)
1959        return resp
async def fetch_posts_and_parent_awaiting( self, child_id: int, /, *, show_banned: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
1961    async def fetch_posts_and_parent_awaiting(
1962        self, child_id: int, /, *, show_banned: str | None = None
1963    ) -> typedefs.JSONObject:
1964        resp = await self._request(
1965            RequestMethod.GET,
1966            f"Forum/GetPostAndParentAwaitingApproval/{child_id}/",
1967            json={"showbanned": show_banned},
1968        )
1969        assert isinstance(resp, dict)
1970        return resp
async def fetch_topic_for_content(self, content_id: int, /) -> int:
1972    async def fetch_topic_for_content(self, content_id: int, /) -> int:
1973        resp = await self._request(
1974            RequestMethod.GET, f"Forum/GetTopicForContent/{content_id}/"
1975        )
1976        assert isinstance(resp, int)
1977        return resp
async def fetch_forum_tag_suggestions(self, partial_tag: str, /) -> collections.abc.Mapping[str, typing.Any]:
1979    async def fetch_forum_tag_suggestions(
1980        self, partial_tag: str, /
1981    ) -> typedefs.JSONObject:
1982        resp = await self._request(
1983            RequestMethod.GET,
1984            "Forum/GetForumTagSuggestions/",
1985            json={"partialtag": partial_tag},
1986        )
1987        assert isinstance(resp, dict)
1988        return resp
async def fetch_poll(self, topic_id: int, /) -> collections.abc.Mapping[str, typing.Any]:
1990    async def fetch_poll(self, topic_id: int, /) -> typedefs.JSONObject:
1991        resp = await self._request(RequestMethod.GET, f"Forum/Poll/{topic_id}/")
1992        assert isinstance(resp, dict)
1993        return resp
async def fetch_recruitment_thread_summaries( self) -> collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]:
1995    async def fetch_recruitment_thread_summaries(self) -> typedefs.JSONArray:
1996        resp = await self._request(RequestMethod.POST, "Forum/Recruit/Summaries/")
1997        assert isinstance(resp, list)
1998        return resp
async def fetch_available_avatars(self) -> collections.abc.Mapping[str, int]:
2016    async def fetch_available_avatars(self) -> collections.Mapping[str, int]:
2017        resp = await self._request(RequestMethod.GET, "GroupV2/GetAvailableAvatars/")
2018        assert isinstance(resp, dict)
2019        return resp
async def fetch_user_clan_invite_setting( self, access_token: str, /, membership_type: MembershipType | int) -> bool:
2021    async def fetch_user_clan_invite_setting(
2022        self,
2023        access_token: str,
2024        /,
2025        membership_type: enums.MembershipType | int,
2026    ) -> bool:
2027        resp = await self._request(
2028            RequestMethod.GET,
2029            f"GroupV2/GetUserClanInviteSetting/{int(membership_type)}/",
2030            auth=access_token,
2031        )
2032        assert isinstance(resp, bool)
2033        return resp
async def fetch_banned_group_members( self, access_token: str, group_id: int, /, *, page: int = 1) -> collections.abc.Mapping[str, typing.Any]:
2035    async def fetch_banned_group_members(
2036        self, access_token: str, group_id: int, /, *, page: int = 1
2037    ) -> typedefs.JSONObject:
2038        resp = await self._request(
2039            RequestMethod.GET,
2040            f"GroupV2/{group_id}/Banned/?currentpage={page}",
2041            auth=access_token,
2042        )
2043        assert isinstance(resp, dict)
2044        return resp
async def fetch_pending_group_memberships( self, access_token: str, group_id: int, /, *, current_page: int = 1) -> collections.abc.Mapping[str, typing.Any]:
2046    async def fetch_pending_group_memberships(
2047        self, access_token: str, group_id: int, /, *, current_page: int = 1
2048    ) -> typedefs.JSONObject:
2049        resp = await self._request(
2050            RequestMethod.GET,
2051            f"GroupV2/{group_id}/Members/Pending/?currentpage={current_page}",
2052            auth=access_token,
2053        )
2054        assert isinstance(resp, dict)
2055        return resp
async def fetch_invited_group_memberships( self, access_token: str, group_id: int, /, *, current_page: int = 1) -> collections.abc.Mapping[str, typing.Any]:
2057    async def fetch_invited_group_memberships(
2058        self, access_token: str, group_id: int, /, *, current_page: int = 1
2059    ) -> typedefs.JSONObject:
2060        resp = await self._request(
2061            RequestMethod.GET,
2062            f"GroupV2/{group_id}/Members/InvitedIndividuals/?currentpage={current_page}",
2063            auth=access_token,
2064        )
2065        assert isinstance(resp, dict)
2066        return resp
async def invite_member_to_group( self, access_token: str, /, group_id: int, membership_id: int, membership_type: MembershipType | int, *, message: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
2068    async def invite_member_to_group(
2069        self,
2070        access_token: str,
2071        /,
2072        group_id: int,
2073        membership_id: int,
2074        membership_type: enums.MembershipType | int,
2075        *,
2076        message: str | None = None,
2077    ) -> typedefs.JSONObject:
2078        resp = await self._request(
2079            RequestMethod.POST,
2080            f"GroupV2/{group_id}/Members/IndividualInvite/{int(membership_type)}/{membership_id}/",
2081            auth=access_token,
2082            json={"message": str(message)},
2083        )
2084        assert isinstance(resp, dict)
2085        return resp
async def cancel_group_member_invite( self, access_token: str, /, group_id: int, membership_id: int, membership_type: MembershipType | int) -> collections.abc.Mapping[str, typing.Any]:
2087    async def cancel_group_member_invite(
2088        self,
2089        access_token: str,
2090        /,
2091        group_id: int,
2092        membership_id: int,
2093        membership_type: enums.MembershipType | int,
2094    ) -> typedefs.JSONObject:
2095        resp = await self._request(
2096            RequestMethod.POST,
2097            f"GroupV2/{group_id}/Members/IndividualInviteCancel/{int(membership_type)}/{membership_id}/",
2098            auth=access_token,
2099        )
2100        assert isinstance(resp, dict)
2101        return resp
async def fetch_historical_definition(self) -> collections.abc.Mapping[str, typing.Any]:
2103    async def fetch_historical_definition(self) -> typedefs.JSONObject:
2104        resp = await self._request(RequestMethod.GET, "Destiny2/Stats/Definition/")
2105        assert isinstance(resp, dict)
2106        return resp
async def fetch_historical_stats( self, character_id: int, membership_id: int, membership_type: MembershipType | int, day_start: datetime.datetime, day_end: datetime.datetime, groups: list[aiobungie.internal.enums.StatsGroupType | int], modes: collections.abc.Sequence[GameMode | int], *, period_type: aiobungie.internal.enums.PeriodType = <PeriodType.ALL_TIME: 2>) -> collections.abc.Mapping[str, typing.Any]:
2108    async def fetch_historical_stats(
2109        self,
2110        character_id: int,
2111        membership_id: int,
2112        membership_type: enums.MembershipType | int,
2113        day_start: datetime.datetime,
2114        day_end: datetime.datetime,
2115        groups: list[enums.StatsGroupType | int],
2116        modes: collections.Sequence[enums.GameMode | int],
2117        *,
2118        period_type: enums.PeriodType = enums.PeriodType.ALL_TIME,
2119    ) -> typedefs.JSONObject:
2120        end, start = time.parse_date_range(day_end, day_start)
2121        resp = await self._request(
2122            RequestMethod.GET,
2123            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/",
2124            json={
2125                "dayend": end,
2126                "daystart": start,
2127                "groups": [str(int(group)) for group in groups],
2128                "modes": [str(int(mode)) for mode in modes],
2129                "periodType": int(period_type),
2130            },
2131        )
2132        assert isinstance(resp, dict)
2133        return resp

Fetch historical stats for a specific membership character.

Parameters
  • character_id (int): The character ID to return the stats for.
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
  • day_start (datetime.datetime): The start of the day to return the stats for.
  • day_end (datetime.datetime): The end of the day to return the stats for.
  • groups (list[aiobungie.StatsGroupType]): A list of stats groups to return.
  • modes (list[aiobungie.GameMode | int]): A list of game modes to return.
  • period_type (aiobungie.enums.PeriodType): The period type to return the stats for. This will return ALL_TIME by default if not modified.
Returns
async def fetch_historical_stats_for_account( self, membership_id: int, membership_type: MembershipType | int, groups: list[aiobungie.internal.enums.StatsGroupType | int]) -> collections.abc.Mapping[str, typing.Any]:
2135    async def fetch_historical_stats_for_account(
2136        self,
2137        membership_id: int,
2138        membership_type: enums.MembershipType | int,
2139        groups: list[enums.StatsGroupType | int],
2140    ) -> typedefs.JSONObject:
2141        resp = await self._request(
2142            RequestMethod.GET,
2143            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Stats/",
2144            json={"groups": [str(int(group)) for group in groups]},
2145        )
2146        assert isinstance(resp, dict)
2147        return resp

Fetch historical stats for an account's membership.

Parameters
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
  • groups (list[aiobungie.StatsGroupType]): A list of stats groups to return.
Returns
async def fetch_aggregated_activity_stats( self, character_id: int, membership_id: int, membership_type: MembershipType | int, /) -> collections.abc.Mapping[str, typing.Any]:
2149    async def fetch_aggregated_activity_stats(
2150        self,
2151        character_id: int,
2152        membership_id: int,
2153        membership_type: enums.MembershipType | int,
2154        /,
2155    ) -> typedefs.JSONObject:
2156        resp = await self._request(
2157            RequestMethod.GET,
2158            f"Destiny2/{int(membership_type)}/Account/{membership_id}/"
2159            f"Character/{character_id}/Stats/AggregateActivityStats/",
2160        )
2161        assert isinstance(resp, dict)
2162        return resp

Fetch aggregated activity stats for a specific membership character.

Parameters
  • character_id (int): The character ID to return the stats for.
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
Returns
async def equip_loadout( self, access_token: str, /, loadout_index: int, character_id: int, membership_type: MembershipType | int) -> None:
2164    async def equip_loadout(
2165        self,
2166        access_token: str,
2167        /,
2168        loadout_index: int,
2169        character_id: int,
2170        membership_type: enums.MembershipType | int,
2171    ) -> None:
2172        response = await self._request(
2173            RequestMethod.POST,
2174            "Destiny2/Actions/Loadouts/EquipLoadout/",
2175            json={
2176                "loadoutIndex": loadout_index,
2177                "characterId": character_id,
2178                "membership_type": int(membership_type),
2179            },
2180            auth=access_token,
2181        )
2182        assert isinstance(response, int)

Equip a loadout. Your character must be in a Social space, Orbit or Offline while performing this operation.

This operation requires MoveEquipDestinyItems OAuth2 scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • loadout_index (int): The index of the loadout to use.
  • character_id (int): The character ID to equip the loadout to.
  • membership_type (aiobungie.MembershipType | int): The membership type of the account.
async def snapshot_loadout( self, access_token: str, /, loadout_index: int, character_id: int, membership_type: MembershipType | int, *, color_hash: int | None = None, icon_hash: int | None = None, name_hash: int | None = None) -> None:
2184    async def snapshot_loadout(
2185        self,
2186        access_token: str,
2187        /,
2188        loadout_index: int,
2189        character_id: int,
2190        membership_type: enums.MembershipType | int,
2191        *,
2192        color_hash: int | None = None,
2193        icon_hash: int | None = None,
2194        name_hash: int | None = None,
2195    ) -> None:
2196        response = await self._request(
2197            RequestMethod.POST,
2198            "Destiny2/Actions/Loadouts/SnapshotLoadout/",
2199            auth=access_token,
2200            json={
2201                "colorHash": color_hash,
2202                "iconHash": icon_hash,
2203                "nameHash": name_hash,
2204                "loadoutIndex": loadout_index,
2205                "characterId": character_id,
2206                "membershipType": int(membership_type),
2207            },
2208        )
2209        assert isinstance(response, int)

Snapshot a loadout with the currently equipped items.

This operation requires MoveEquipDestinyItems OAuth2 scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • loadout_index (int): The index of the loadout to use.
  • character_id (int): The character ID to equip the loadout to.
  • membership_type (aiobungie.MembershipType | int): The membership type of the account.
Other Parameters
  • color_hash (int | None): ...
  • icon_hash (int | None): ...
  • name_hash (int | None): ...
async def update_loadout( self, access_token: str, /, loadout_index: int, character_id: int, membership_type: MembershipType | int, *, color_hash: int | None = None, icon_hash: int | None = None, name_hash: int | None = None) -> None:
2211    async def update_loadout(
2212        self,
2213        access_token: str,
2214        /,
2215        loadout_index: int,
2216        character_id: int,
2217        membership_type: enums.MembershipType | int,
2218        *,
2219        color_hash: int | None = None,
2220        icon_hash: int | None = None,
2221        name_hash: int | None = None,
2222    ) -> None:
2223        response = await self._request(
2224            RequestMethod.POST,
2225            "Destiny2/Actions/Loadouts/UpdateLoadoutIdentifiers/",
2226            auth=access_token,
2227            json={
2228                "colorHash": color_hash,
2229                "iconHash": icon_hash,
2230                "nameHash": name_hash,
2231                "loadoutIndex": loadout_index,
2232                "characterId": character_id,
2233                "membershipType": int(membership_type),
2234            },
2235        )
2236        assert isinstance(response, int)

Update the loadout. Color, Icon and Name.

This operation requires MoveEquipDestinyItems OAuth2 scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • loadout_index (int): The index of the loadout to use.
  • character_id (int): The character ID to equip the loadout to.
  • membership_type (aiobungie.MembershipType | int): The membership type of the account.
Other Parameters
  • color_hash (int | None): The new color hash of the loadout to update.
  • icon_hash (int | None): The new icon hash of the loadout to update.
  • name_hash (int | None): The new name hash of the loadout to update.
async def clear_loadout( self, access_token: str, /, loadout_index: int, character_id: int, membership_type: MembershipType | int) -> None:
2238    async def clear_loadout(
2239        self,
2240        access_token: str,
2241        /,
2242        loadout_index: int,
2243        character_id: int,
2244        membership_type: enums.MembershipType | int,
2245    ) -> None:
2246        response = await self._request(
2247            RequestMethod.POST,
2248            "Destiny2/Actions/Loadouts/ClearLoadout/",
2249            json={
2250                "loadoutIndex": loadout_index,
2251                "characterId": character_id,
2252                "membership_type": int(membership_type),
2253            },
2254            auth=access_token,
2255        )
2256        assert isinstance(response, int)

Clear the identifiers and items of a loadout.

This operation requires MoveEquipDestinyItems OAuth2 scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • loadout_index (int): The index of the loadout to use.
  • character_id (int): The character ID to equip the loadout to.
  • membership_type (aiobungie.MembershipType | int): The membership type of the account.
class RESTPool:
191class RESTPool:
192    """Pool of `RESTClient` instances.
193
194    This allows to create multiple instances of `RESTClient`s that can be acquired
195    which share the same TCP connector.
196
197    Example
198    -------
199    ```py
200    import aiobungie
201
202    client_pool = aiobungie.RESTPool("token", client_id=1234, client_secret='secret')
203
204    # Using a context manager to acquire an instance
205    # from the pool and close it after.
206    async with client_pool.acquire() as rest:
207        ...
208    ```
209
210    Parameters
211    ----------
212    token : `str`
213        A valid application token from Bungie's developer portal.
214
215    Other Parameters
216    ----------------
217    max_retries : `int`
218        The max retries number to retry if the request hit a `5xx` status code.
219    client_secret : `str | None`
220        An optional application client secret,
221        This is only needed if you're fetching OAuth2 tokens with this client.
222    client_id : `int | None`
223        An optional application client id,
224        This is only needed if you're fetching OAuth2 tokens with this client.
225    enable_debugging : `bool | str`
226        Whether to enable logging responses or not.
227
228    Logging Levels
229    --------------
230    * `False`: This will disable logging.
231    * `True`: This will set the level to `DEBUG` and enable logging minimal information.
232    Like the response status, route, taken time and so on.
233    * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information.
234    """
235
236    __slots__ = (
237        "_token",
238        "_max_retries",
239        "_client_secret",
240        "_client_id",
241        "_metadata",
242        "_enable_debug",
243        "_client_session",
244        "_loads",
245        "_dumps",
246    )
247
248    # Looks like mypy doesn't like this.
249    if typing.TYPE_CHECKING:
250        _enable_debug: typing.Literal["TRACE"] | bool | int
251
252    def __init__(
253        self,
254        token: str,
255        /,
256        *,
257        client_secret: str | None = None,
258        client_id: int | None = None,
259        client_session: aiohttp.ClientSession | None = None,
260        dumps: typedefs.Dumps = helpers.dumps,
261        loads: typedefs.Loads = helpers.loads,
262        max_retries: int = 4,
263        enable_debugging: typing.Literal["TRACE"] | bool | int = False,
264    ) -> None:
265        self._client_secret = client_secret
266        self._client_id = client_id
267        self._token = token
268        self._max_retries = max_retries
269        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
270        self._enable_debug = enable_debugging
271        self._client_session = client_session
272        self._loads = loads
273        self._dumps = dumps
274
275    @property
276    def client_id(self) -> int | None:
277        return self._client_id
278
279    @property
280    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
281        """Pool's Metadata. This is different from client instance metadata."""
282        return self._metadata
283
284    @typing.final
285    def acquire(self) -> RESTClient:
286        """Acquires a new `RESTClient` instance from this pool.
287
288        Returns
289        -------
290        `RESTClient`
291            An instance of a `RESTClient`.
292        """
293        return RESTClient(
294            self._token,
295            client_secret=self._client_secret,
296            client_id=self._client_id,
297            loads=self._loads,
298            dumps=self._dumps,
299            max_retries=self._max_retries,
300            enable_debugging=self._enable_debug,
301            client_session=self._client_session,
302        )

Pool of RESTClient instances.

This allows to create multiple instances of RESTClients that can be acquired which share the same TCP connector.

Example
import aiobungie

client_pool = aiobungie.RESTPool("token", client_id=1234, client_secret='secret')

# Using a context manager to acquire an instance
# from the pool and close it after.
async with client_pool.acquire() as rest:
    ...
Parameters
  • token (str): A valid application token from Bungie's developer portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • client_secret (str | None): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (int | None): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
  • enable_debugging (bool | str): Whether to enable logging responses or not.
Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information. Like the response status, route, taken time and so on.
  • "TRACE" | TRACE: This will log the response headers along with the minimal information.
RESTPool( token: str, /, *, client_secret: str | None = None, client_id: int | None = None, client_session: aiohttp.client.ClientSession | None = None, dumps: collections.abc.Callable[[collections.abc.Mapping[str, typing.Any] | collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]], bytes] = <function dumps>, loads: collections.abc.Callable[[str | bytes], collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]] | collections.abc.Mapping[str, typing.Any]] = <function loads>, max_retries: int = 4, enable_debugging: Union[Literal['TRACE'], bool, int] = False)
252    def __init__(
253        self,
254        token: str,
255        /,
256        *,
257        client_secret: str | None = None,
258        client_id: int | None = None,
259        client_session: aiohttp.ClientSession | None = None,
260        dumps: typedefs.Dumps = helpers.dumps,
261        loads: typedefs.Loads = helpers.loads,
262        max_retries: int = 4,
263        enable_debugging: typing.Literal["TRACE"] | bool | int = False,
264    ) -> None:
265        self._client_secret = client_secret
266        self._client_id = client_id
267        self._token = token
268        self._max_retries = max_retries
269        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
270        self._enable_debug = enable_debugging
271        self._client_session = client_session
272        self._loads = loads
273        self._dumps = dumps
client_id: int | None
metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

Pool's Metadata. This is different from client instance metadata.

@typing.final
def acquire(self) -> RESTClient:
284    @typing.final
285    def acquire(self) -> RESTClient:
286        """Acquires a new `RESTClient` instance from this pool.
287
288        Returns
289        -------
290        `RESTClient`
291            An instance of a `RESTClient`.
292        """
293        return RESTClient(
294            self._token,
295            client_secret=self._client_secret,
296            client_id=self._client_id,
297            loads=self._loads,
298            dumps=self._dumps,
299            max_retries=self._max_retries,
300            enable_debugging=self._enable_debug,
301            client_session=self._client_session,
302        )

Acquires a new RESTClient instance from this pool.

Returns
@typing.final
class Race(builtins.int, aiobungie.Enum):
484@typing.final
485class Race(int, Enum):
486    """An Enum for Destiny races."""
487
488    HUMAN = 0
489    AWOKEN = 1
490    EXO = 2
491    UNKNOWN = 3

An Enum for Destiny races.

HUMAN = <Race.HUMAN: 0>
AWOKEN = <Race.AWOKEN: 1>
EXO = <Race.EXO: 2>
UNKNOWN = <Race.UNKNOWN: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Raid(builtins.int, aiobungie.Enum):
132@typing.final
133class Raid(int, Enum):
134    """An Enum for all available raids in Destiny 2."""
135
136    DSC = 910380154
137    """Deep Stone Crypt"""
138
139    LW = 2122313384
140    """Last Wish"""
141
142    VOG = 3881495763
143    """Normal Valut of Glass"""
144
145    GOS = 3458480158
146    """Garden Of Salvation"""

An Enum for all available raids in Destiny 2.

DSC = <Raid.DSC: 910380154>

Deep Stone Crypt

LW = <Raid.LW: 2122313384>

Last Wish

VOG = <Raid.VOG: 3881495763>

Normal Valut of Glass

GOS = <Raid.GOS: 3458480158>

Garden Of Salvation

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class RateLimitedError(aiobungie.HTTPError):
251@attrs.define(auto_exc=True)
252class RateLimitedError(HTTPError):
253    """Raised when too many request status code is returned."""
254
255    http_status: http.HTTPStatus = attrs.field(
256        default=http.HTTPStatus.TOO_MANY_REQUESTS, init=False
257    )
258    """The request response http status."""
259
260    url: typedefs.StrOrURL
261    """The URL/endpoint caused this error."""
262
263    body: typing.Any
264    """The response body."""
265
266    retry_after: float = attrs.field(default=0.0)
267    """The amount of seconds you need to wait before retrying to requests."""
268
269    message: str = attrs.field(init=False)
270    """A Bungie human readable message describes the cause of the error."""
271
272    @message.default  # type: ignore
273    def _(self) -> str:
274        return f"You're ratelimited for {self.retry_after}, Endpoint: {self.url}. Slow down!"
275
276    def __str__(self) -> str:
277        return self.message

Raised when too many request status code is returned.

RateLimitedError(url: Union[str, yarl.URL], body: Any, retry_after: float = 0.0)
2def __init__(self, url, body, retry_after=attr_dict['retry_after'].default):
3    self.http_status = attr_dict['http_status'].default
4    self.url = url
5    self.body = body
6    self.retry_after = retry_after
7    self.message = __attr_factory_message(self)
8    BaseException.__init__(self, self.url,self.body,self.retry_after)

Method generated by attrs for class RateLimitedError.

http_status: http.HTTPStatus

The request response http status.

url: Union[str, yarl.URL]

The URL/endpoint caused this error.

body: Any

The response body.

retry_after: float

The amount of seconds you need to wait before retrying to requests.

message: str

A Bungie human readable message describes the cause of the error.

Inherited Members
builtins.BaseException
with_traceback
args
@typing.final
class RecordState(aiobungie.Flag):
47@typing.final
48class RecordState(enums.Flag):
49    """An enum for records component states."""
50
51    NONE = 0
52    REDEEMED = 1 << 0
53    UNAVAILABLE = 1 << 1
54    OBJECTIVE_NOT_COMPLETED = 1 << 2
55    OBSCURED = 1 << 3
56    INVISIBLE = 1 << 4
57    ENTITLEMENT_UNOWNED = 1 << 5
58    CAN_EQUIP_TITLE = 1 << 6

An enum for records component states.

NONE = <RecordState.NONE: 0>
REDEEMED = <RecordState.REDEEMED: 1>
UNAVAILABLE = <RecordState.UNAVAILABLE: 2>
OBJECTIVE_NOT_COMPLETED = <RecordState.OBJECTIVE_NOT_COMPLETED: 4>
OBSCURED = <RecordState.OBSCURED: 8>
INVISIBLE = <RecordState.INVISIBLE: 16>
ENTITLEMENT_UNOWNED = <RecordState.ENTITLEMENT_UNOWNED: 32>
CAN_EQUIP_TITLE = <RecordState.CAN_EQUIP_TITLE: 64>
Inherited Members
Flag
name
value
@typing.final
class Relationship(builtins.int, aiobungie.Enum):
679@typing.final
680class Relationship(int, Enum):
681    """An enum for bungie friends relationship types."""
682
683    UNKNOWN = 0
684    FRIEND = 1
685    INCOMING_REQUEST = 2
686    OUTGOING_REQUEST = 3

An enum for bungie friends relationship types.

UNKNOWN = <Relationship.UNKNOWN: 0>
FRIEND = <Relationship.FRIEND: 1>
INCOMING_REQUEST = <Relationship.INCOMING_REQUEST: 2>
OUTGOING_REQUEST = <Relationship.OUTGOING_REQUEST: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class RequestMethod(builtins.str, aiobungie.Enum):
176class RequestMethod(str, enums.Enum):
177    """HTTP request methods enum."""
178
179    GET = "GET"
180    """GET methods."""
181    POST = "POST"
182    """POST methods."""
183    PUT = "PUT"
184    """PUT methods."""
185    PATCH = "PATCH"
186    """PATCH methods."""
187    DELETE = "DELETE"
188    """DELETE methods"""

HTTP request methods enum.

GET = <RequestMethod.GET: GET>

GET methods.

POST = <RequestMethod.POST: POST>

POST methods.

PUT = <RequestMethod.PUT: PUT>

PUT methods.

PATCH = <RequestMethod.PATCH: PATCH>

PATCH methods.

DELETE = <RequestMethod.DELETE: DELETE>

DELETE methods

Inherited Members
Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
@attrs.define(auto_exc=True)
class ResponseError(aiobungie.HTTPException):
246@attrs.define(auto_exc=True)
247class ResponseError(HTTPException):
248    """Exception for other HTTP response errors."""

Exception for other HTTP response errors.

ResponseError( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class ResponseError.

Inherited Members
HTTPException
error_code
http_status
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class Stat(builtins.int, aiobungie.Enum):
506@typing.final
507class Stat(int, Enum):
508    """An Enum for Destiny 2 character stats."""
509
510    NONE = 0
511    MOBILITY = 2996146975
512    RESILIENCE = 392767087
513    RECOVERY = 1943323491
514    DISCIPLINE = 1735777505
515    INTELLECT = 144602215
516    STRENGTH = 4244567218
517    LIGHT_POWER = 1935470627

An Enum for Destiny 2 character stats.

NONE = <Stat.NONE: 0>
MOBILITY = <Stat.MOBILITY: 2996146975>
RESILIENCE = <Stat.RESILIENCE: 392767087>
RECOVERY = <Stat.RECOVERY: 1943323491>
DISCIPLINE = <Stat.DISCIPLINE: 1735777505>
INTELLECT = <Stat.INTELLECT: 144602215>
STRENGTH = <Stat.STRENGTH: 4244567218>
LIGHT_POWER = <Stat.LIGHT_POWER: 1935470627>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
TRACE = 5
@typing.final
class TierType(builtins.int, aiobungie.Enum):
621@typing.final
622class TierType(int, Enum):
623    """An enum for a Destiny 2 item tier type."""
624
625    UNKNOWN = 0
626    CURRENCY = 1
627    BASIC = 2
628    COMMON = 3
629    RARE = 4
630    SUPERIOR = 5
631    EXOTIC = 6

An enum for a Destiny 2 item tier type.

UNKNOWN = <TierType.UNKNOWN: 0>
CURRENCY = <TierType.CURRENCY: 1>
BASIC = <TierType.BASIC: 2>
COMMON = <TierType.COMMON: 3>
RARE = <TierType.RARE: 4>
SUPERIOR = <TierType.SUPERIOR: 5>
EXOTIC = <TierType.EXOTIC: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class TransferStatus(aiobungie.Flag):
731@typing.final
732class TransferStatus(Flag):
733    """An enum for items transfer statuses."""
734
735    CAN_TRANSFER = 0
736    """The item can be transferred."""
737    IS_EQUIPPED = 1 << 0
738    """You can't transfer since the item is equipped."""
739    NOT_TRASNFERRABLE = 1 << 1
740    """This item can not be transferred."""
741    COULD_BE_TRANSFERRED = 1 << 2
742    """You can transfer the item. But the place you're trying to put it at has no space for it."""

An enum for items transfer statuses.

CAN_TRANSFER = <TransferStatus.CAN_TRANSFER: 0>

The item can be transferred.

IS_EQUIPPED = <TransferStatus.IS_EQUIPPED: 1>

You can't transfer since the item is equipped.

NOT_TRASNFERRABLE = <TransferStatus.NOT_TRASNFERRABLE: 2>

This item can not be transferred.

COULD_BE_TRANSFERRED = <TransferStatus.COULD_BE_TRANSFERRED: 4>

You can transfer the item. But the place you're trying to put it at has no space for it.

Inherited Members
Flag
name
value
@attrs.define(auto_exc=True)
class Unauthorized(aiobungie.HTTPException):
155@attrs.define(auto_exc=True)
156class Unauthorized(HTTPException):
157    """An exception that's raised when trying to make unauthorized call to a resource and it returns 404."""
158
159    http_status: http.HTTPStatus = attrs.field(
160        default=http.HTTPStatus.UNAUTHORIZED, init=False
161    )

An exception that's raised when trying to make unauthorized call to a resource and it returns 404.

Unauthorized( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class Unauthorized.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class ValueUIStyle(builtins.int, aiobungie.Enum):
74@typing.final
75class ValueUIStyle(int, enums.Enum):
76    AUTOMATIC = 0
77    FRACTION = 1
78    CHECK_BOX = 2
79    PERCENTAGE = 3
80    DATETIME = 4
81    FRACTION_FLOAT = 5
82    INTEGER = 6
83    TIME_DURATION = 7
84    HIDDEN = 8
85    MULTIPLIER = 9
86    GREEN_PIPS = 10
87    RED_PIPS = 11
88    EXPLICIT_PERCENTAGE = 12
89    RAW_FLOAT = 13
90    LEVEL_AND_REWARD = 14

An enumeration.

AUTOMATIC = <ValueUIStyle.AUTOMATIC: 0>
FRACTION = <ValueUIStyle.FRACTION: 1>
CHECK_BOX = <ValueUIStyle.CHECK_BOX: 2>
PERCENTAGE = <ValueUIStyle.PERCENTAGE: 3>
DATETIME = <ValueUIStyle.DATETIME: 4>
FRACTION_FLOAT = <ValueUIStyle.FRACTION_FLOAT: 5>
INTEGER = <ValueUIStyle.INTEGER: 6>
TIME_DURATION = <ValueUIStyle.TIME_DURATION: 7>
HIDDEN = <ValueUIStyle.HIDDEN: 8>
MULTIPLIER = <ValueUIStyle.MULTIPLIER: 9>
GREEN_PIPS = <ValueUIStyle.GREEN_PIPS: 10>
RED_PIPS = <ValueUIStyle.RED_PIPS: 11>
EXPLICIT_PERCENTAGE = <ValueUIStyle.EXPLICIT_PERCENTAGE: 12>
RAW_FLOAT = <ValueUIStyle.RAW_FLOAT: 13>
LEVEL_AND_REWARD = <ValueUIStyle.LEVEL_AND_REWARD: 14>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Vendor(builtins.int, aiobungie.Enum):
229@typing.final
230class Vendor(int, Enum):
231    """An Enum for all available vendors in Destiny 2."""
232
233    ZAVALA = 69482069
234    XUR = 2190858386
235    BANSHE = 672118013
236    SPIDER = 863940356
237    SHAXX = 3603221665
238    KADI = 529635856
239    """Postmaster exo."""
240    YUNA = 1796504621
241    """Asia servers only."""
242    EVERVERSE = 3361454721
243    AMANDA = 460529231
244    """Amanda holiday"""
245    CROW = 3611983588
246    HAWTHORNE = 3347378076
247    ADA1 = 350061650
248    DRIFTER = 248695599
249    IKORA = 1976548992
250    SAINT = 765357505
251    """Saint-14"""
252    ERIS_MORN = 1616085565
253    SHAW_HAWN = 1816541247
254    """COSMODROME Guy"""
255    VARIKS = 2531198101

An Enum for all available vendors in Destiny 2.

ZAVALA = <Vendor.ZAVALA: 69482069>
XUR = <Vendor.XUR: 2190858386>
BANSHE = <Vendor.BANSHE: 672118013>
SPIDER = <Vendor.SPIDER: 863940356>
SHAXX = <Vendor.SHAXX: 3603221665>
KADI = <Vendor.KADI: 529635856>

Postmaster exo.

YUNA = <Vendor.YUNA: 1796504621>

Asia servers only.

EVERVERSE = <Vendor.EVERVERSE: 3361454721>
AMANDA = <Vendor.AMANDA: 460529231>

Amanda holiday

CROW = <Vendor.CROW: 3611983588>
HAWTHORNE = <Vendor.HAWTHORNE: 3347378076>
ADA1 = <Vendor.ADA1: 350061650>
DRIFTER = <Vendor.DRIFTER: 248695599>
IKORA = <Vendor.IKORA: 1976548992>
SAINT = <Vendor.SAINT: 765357505>

Saint-14

ERIS_MORN = <Vendor.ERIS_MORN: 1616085565>
SHAW_HAWN = <Vendor.SHAW_HAWN: 1816541247>

COSMODROME Guy

VARIKS = <Vendor.VARIKS: 2531198101>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class WeaponType(builtins.int, aiobungie.Enum):
520@typing.final
521class WeaponType(int, Enum):
522    """Enums for The three Destiny Weapon Types"""
523
524    NONE = 0
525    KINETIC = 1498876634
526    ENERGY = 2465295065
527    POWER = 953998645

Enums for The three Destiny Weapon Types

NONE = <WeaponType.NONE: 0>
KINETIC = <WeaponType.KINETIC: 1498876634>
ENERGY = <WeaponType.ENERGY: 2465295065>
POWER = <WeaponType.POWER: 953998645>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
annotations = _Feature((3, 7, 0, 'beta', 1), (3, 11, 0, 'alpha', 0), 16777216)
def iter( iterable: collections.abc.Iterable[~Item]) -> Iterator[~Item]:
584def iter(
585    iterable: collections.Iterable[Item],
586) -> Iterator[Item]:
587    """Transform an iterable into an flat iterator.
588
589    Example
590    -------
591    ```py
592    sequence = [1,2,3]
593    for item in aiobungie.iter(sequence).reversed():
594        print(item)
595    # 3
596    # 2
597    # 1
598    ```
599
600    Parameters
601    ----------
602    iterable: `typing.Iterable[Item]`
603        The iterable to convert.
604
605    Raises
606    ------
607    `StopIteration`
608        If no elements are left in the iterator.
609    """
610    return Iterator(iterable)

Transform an iterable into an flat iterator.

Example
sequence = [1,2,3]
for item in aiobungie.iter(sequence).reversed():
    print(item)
# 3
# 2
# 1
Parameters
  • iterable (typing.Iterable[Item]): The iterable to convert.
Raises
  • StopIteration: If no elements are left in the iterator.
async def raise_error( response: aiohttp.client_reqrep.ClientResponse) -> AiobungieError:
280async def raise_error(response: aiohttp.ClientResponse) -> AiobungieError:
281    """Generates and raise exceptions on error responses."""
282
283    # Not a JSON response, raise immediately.
284
285    # Also Bungie sometimes get funky and return HTML instead of JSON when making an authorized
286    # request with a dummy access token. I can't really do anything about this..
287    if response.content_type != "application/json":
288        return HTTPError(
289            f"Expected JSON content but got {response.content_type!s}, {response.real_url!s}",
290            http.HTTPStatus.UNSUPPORTED_MEDIA_TYPE,
291        )
292
293    body: collections.Mapping[str, typing.Any] = helpers.loads(await response.read())  # type: ignore
294    message: str = body.get("Message", "UNDEFINED_MESSAGE")
295    error_status: str = body.get("ErrorStatus", "UNDEFINED_ERROR_STATUS")
296    message_data: dict[str, str] = body.get("MessageData", {})
297    throttle_seconds: int = body.get("ThrottleSeconds", 0)
298    error_code: int = body.get("ErrorCode", 0)
299
300    # Standard HTTP status.
301    match response.status:
302        case http.HTTPStatus.NOT_FOUND:
303            return NotFound(
304                message=message,
305                error_code=error_code,
306                throttle_seconds=throttle_seconds,
307                url=str(response.real_url),
308                body=body,
309                headers=response.headers,
310                error_status=error_status,
311                message_data=message_data,
312            )
313
314        case http.HTTPStatus.FORBIDDEN:
315            return Forbidden(
316                message=message,
317                error_code=error_code,
318                throttle_seconds=throttle_seconds,
319                url=str(response.real_url),
320                body=body,
321                headers=response.headers,
322                error_status=error_status,
323                message_data=message_data,
324            )
325
326        case http.HTTPStatus.UNAUTHORIZED:
327            return Unauthorized(
328                message=message,
329                error_code=error_code,
330                throttle_seconds=throttle_seconds,
331                url=str(response.real_url),
332                body=body,
333                headers=response.headers,
334                error_status=error_status,
335                message_data=message_data,
336            )
337
338        case http.HTTPStatus.BAD_REQUEST:
339            # Membership needs to be alone.
340            if error_status == "InvalidParameters":
341                return MembershipTypeError(
342                    message=message,
343                    body=body,
344                    headers=response.headers,
345                    url=str(response.url),
346                    membership_type=message_data["membershipType"],
347                    required_membership=message_data["membershipInfo.membershipType"],
348                    membership_id=int(message_data["membershipId"]),
349                )
350            return BadRequest(
351                message=message,
352                body=body,
353                headers=response.headers,
354                url=str(response.url),
355            )
356        case _:
357            status = http.HTTPStatus(response.status)
358
359            if 400 <= status < 500:
360                return ResponseError(
361                    message=message,
362                    error_code=error_code,
363                    throttle_seconds=throttle_seconds,
364                    url=str(response.real_url),
365                    body=body,
366                    headers=response.headers,
367                    error_status=error_status,
368                    message_data=message_data,
369                    http_status=status,
370                )
371
372            # Need to self handle ~5xx errors
373            elif 500 <= status < 600:
374                # No API key or method requires OAuth2 most likely.
375                if error_status in {
376                    "ApiKeyMissingFromRequest",
377                    "WebAuthRequired",
378                    "ApiInvalidOrExpiredKey",
379                    "AuthenticationInvalid",
380                    "AuthorizationCodeInvalid",
381                }:
382                    return Unauthorized(
383                        message=message,
384                        error_code=error_code,
385                        throttle_seconds=throttle_seconds,
386                        url=str(response.real_url),
387                        body=body,
388                        headers=response.headers,
389                        error_status=error_status,
390                        message_data=message_data,
391                    )
392
393                # Anything contains not found.
394                elif (
395                    "NotFound" in error_status
396                    or error_status == "UserCannotFindRequestedUser"
397                ):
398                    return NotFound(
399                        message=message,
400                        error_code=error_code,
401                        throttle_seconds=throttle_seconds,
402                        url=str(response.real_url),
403                        body=body,
404                        headers=response.headers,
405                        error_status=error_status,
406                        message_data=message_data,
407                    )
408
409                # Other 5xx errors.
410                else:
411                    return InternalServerError(
412                        message=message,
413                        error_code=error_code,
414                        throttle_seconds=throttle_seconds,
415                        url=str(response.real_url),
416                        body=body,
417                        headers=response.headers,
418                        error_status=error_status,
419                        message_data=message_data,
420                        http_status=status,
421                    )
422            # Something else.
423            else:
424                return HTTPException(
425                    message=message,
426                    error_code=error_code,
427                    throttle_seconds=throttle_seconds,
428                    url=str(response.real_url),
429                    body=body,
430                    headers=response.headers,
431                    error_status=error_status,
432                    message_data=message_data,
433                    http_status=status,
434                )

Generates and raise exceptions on error responses.

def stringify_http_message(headers: collections.abc.Mapping[str, str]) -> str:
437def stringify_http_message(headers: collections.Mapping[str, str]) -> str:
438    return (
439        "{ \n"
440        + "\n".join(  # noqa: W503
441            f"{f'   {key}'}: {value}"
442            if key not in ("Authorization", "X-API-KEY")
443            else f"   {key}: REDACTED_TOKEN"
444            for key, value in headers.items()
445        )
446        + "\n}"  # noqa: W503
447    )